Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
commit
1e41be45ba
@ -21,6 +21,7 @@ import org.eclipse.californium.core.coap.MediaTypeRegistry;
|
|||||||
import org.eclipse.californium.core.coap.Request;
|
import org.eclipse.californium.core.coap.Request;
|
||||||
import org.eclipse.californium.core.coap.Response;
|
import org.eclipse.californium.core.coap.Response;
|
||||||
import org.eclipse.californium.core.server.resources.CoapExchange;
|
import org.eclipse.californium.core.server.resources.CoapExchange;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.transport.SessionMsgListener;
|
import org.thingsboard.server.common.transport.SessionMsgListener;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||||
import org.thingsboard.server.transport.coap.client.TbCoapClientState;
|
import org.thingsboard.server.transport.coap.client.TbCoapClientState;
|
||||||
@ -52,6 +53,11 @@ public abstract class AbstractSyncSessionCallback implements SessionMsgListener
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDeleted(DeviceId deviceId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) {
|
public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) {
|
||||||
logUnsupportedCommandMessage(toDeviceRequest);
|
logUnsupportedCommandMessage(toDeviceRequest);
|
||||||
|
|||||||
@ -603,6 +603,13 @@ public class DeviceApiController implements TbTransportService {
|
|||||||
responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
|
responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDeleted(DeviceId deviceId) {
|
||||||
|
UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
|
||||||
|
log.trace("[{}] Received device deleted notification for device with id: {}",sessionId, deviceId);
|
||||||
|
responseWriter.setResult(new ResponseEntity<>("Device was deleted!", HttpStatus.FORBIDDEN));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaType parseMediaType(String contentType) {
|
private static MediaType parseMediaType(String contentType) {
|
||||||
|
|||||||
@ -71,7 +71,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
|
|||||||
|
|
||||||
private LeshanServer server;
|
private LeshanServer server;
|
||||||
|
|
||||||
@AfterStartUp
|
@AfterStartUp(order = Integer.MAX_VALUE - 1)
|
||||||
public void init() {
|
public void init() {
|
||||||
this.server = getLhServer();
|
this.server = getLhServer();
|
||||||
/*
|
/*
|
||||||
@ -83,8 +83,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
|
|||||||
*/
|
*/
|
||||||
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
|
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
|
||||||
this.server.coap().getServer().add(otaCoapResource);
|
this.server.coap().getServer().add(otaCoapResource);
|
||||||
this.startLhServer();
|
|
||||||
this.context.setServer(server);
|
this.context.setServer(server);
|
||||||
|
this.startLhServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLhServer() {
|
private void startLhServer() {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.leshan.core.link.LinkParamValue;
|
||||||
import org.eclipse.leshan.core.model.ObjectModel;
|
import org.eclipse.leshan.core.model.ObjectModel;
|
||||||
import org.eclipse.leshan.core.model.ResourceModel;
|
import org.eclipse.leshan.core.model.ResourceModel;
|
||||||
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
|
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
|
||||||
@ -108,7 +109,7 @@ public class LwM2mClient implements Serializable {
|
|||||||
@Getter
|
@Getter
|
||||||
private Long edrxCycle;
|
private Long edrxCycle;
|
||||||
@Getter
|
@Getter
|
||||||
private Registration registration;
|
private transient Registration registration;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private boolean asleep;
|
private boolean asleep;
|
||||||
@ -121,9 +122,9 @@ public class LwM2mClient implements Serializable {
|
|||||||
private boolean firstEdrxDownlink = true;
|
private boolean firstEdrxDownlink = true;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private Set<ContentFormat> clientSupportContentFormats;
|
private transient Set<ContentFormat> clientSupportContentFormats;
|
||||||
@Getter
|
@Getter
|
||||||
private ContentFormat defaultContentFormat;
|
private transient ContentFormat defaultContentFormat;
|
||||||
@Getter
|
@Getter
|
||||||
private final AtomicInteger retryAttempts;
|
private final AtomicInteger retryAttempts;
|
||||||
|
|
||||||
@ -430,9 +431,9 @@ public class LwM2mClient implements Serializable {
|
|||||||
static private Set<ContentFormat> clientSupportContentFormat(Registration registration) {
|
static private Set<ContentFormat> clientSupportContentFormat(Registration registration) {
|
||||||
Set<ContentFormat> contentFormats = new HashSet<>();
|
Set<ContentFormat> contentFormats = new HashSet<>();
|
||||||
contentFormats.add(ContentFormat.DEFAULT);
|
contentFormats.add(ContentFormat.DEFAULT);
|
||||||
String code = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUriReference().equals("/")).findFirst().get().getLinkParams().get("ct").getUnquoted();
|
LinkParamValue ct = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUriReference().equals("/")).findFirst().get().getLinkParams().get("ct");
|
||||||
if (code != null) {
|
if (ct != null) {
|
||||||
Set<ContentFormat> codes = Stream.of(code.replaceAll("\"", "").split(" ", -1))
|
Set<ContentFormat> codes = Stream.of(ct.getUnquoted().replaceAll("\"", "").split(" ", -1))
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.map(Integer::parseInt)
|
.map(Integer::parseInt)
|
||||||
.map(ContentFormat::fromCode)
|
.map(ContentFormat::fromCode)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.eclipse.leshan.core.SecurityMode;
|
import org.eclipse.leshan.core.SecurityMode;
|
||||||
import org.eclipse.leshan.core.node.LwM2mPath;
|
import org.eclipse.leshan.core.node.LwM2mPath;
|
||||||
import org.eclipse.leshan.server.registration.Registration;
|
import org.eclipse.leshan.server.registration.Registration;
|
||||||
|
import org.eclipse.leshan.server.registration.RegistrationStore;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -72,6 +73,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
|
|||||||
private final LwM2MSessionManager sessionManager;
|
private final LwM2MSessionManager sessionManager;
|
||||||
private final TransportDeviceProfileCache deviceProfileCache;
|
private final TransportDeviceProfileCache deviceProfileCache;
|
||||||
private final LwM2MModelConfigService modelConfigService;
|
private final LwM2MModelConfigService modelConfigService;
|
||||||
|
private final RegistrationStore registrationStore;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Lazy
|
@Lazy
|
||||||
@ -118,8 +120,11 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
|
|||||||
|
|
||||||
private void updateFetchedClient(String nodeId, LwM2mClient client) {
|
private void updateFetchedClient(String nodeId, LwM2mClient client) {
|
||||||
boolean updated = false;
|
boolean updated = false;
|
||||||
if (client.getRegistration() != null) {
|
Registration registration = registrationStore.getRegistrationByEndpoint(client.getEndpoint());
|
||||||
lwM2mClientsByRegistrationId.put(client.getRegistration().getId(), client);
|
|
||||||
|
if (registration != null) {
|
||||||
|
client.setRegistration(registration);
|
||||||
|
lwM2mClientsByRegistrationId.put(registration.getId(), client);
|
||||||
}
|
}
|
||||||
if (client.getSession() != null) {
|
if (client.getSession() != null) {
|
||||||
client.refreshSessionId(nodeId);
|
client.refreshSessionId(nodeId);
|
||||||
|
|||||||
@ -111,7 +111,11 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void sendRpcReplyOnError(Exception e) {
|
protected void sendRpcReplyOnError(Exception e) {
|
||||||
reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(e.getMessage()).build());
|
String error = e.getMessage();
|
||||||
|
if (error == null) {
|
||||||
|
error = e.toString();
|
||||||
|
}
|
||||||
|
reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(error).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
|
|||||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||||
import org.thingsboard.server.common.data.TransportPayloadType;
|
import org.thingsboard.server.common.data.TransportPayloadType;
|
||||||
import org.thingsboard.server.common.data.device.profile.MqttTopics;
|
import org.thingsboard.server.common.data.device.profile.MqttTopics;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.data.id.OtaPackageId;
|
import org.thingsboard.server.common.data.id.OtaPackageId;
|
||||||
import org.thingsboard.server.common.data.ota.OtaPackageType;
|
import org.thingsboard.server.common.data.ota.OtaPackageType;
|
||||||
import org.thingsboard.server.common.data.rpc.RpcStatus;
|
import org.thingsboard.server.common.data.rpc.RpcStatus;
|
||||||
@ -1066,4 +1067,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
|
|||||||
deviceSessionCtx.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
|
deviceSessionCtx.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDeleted(DeviceId deviceId) {
|
||||||
|
context.onAuthFailure(address);
|
||||||
|
ChannelHandlerContext ctx = deviceSessionCtx.getChannel();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import io.netty.channel.ChannelFuture;
|
|||||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.data.rpc.RpcStatus;
|
import org.thingsboard.server.common.data.rpc.RpcStatus;
|
||||||
import org.thingsboard.server.common.transport.SessionMsgListener;
|
import org.thingsboard.server.common.transport.SessionMsgListener;
|
||||||
import org.thingsboard.server.common.transport.TransportService;
|
import org.thingsboard.server.common.transport.TransportService;
|
||||||
@ -136,6 +137,11 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
|
|||||||
// This feature is not supported in the TB IoT Gateway yet.
|
// This feature is not supported in the TB IoT Gateway yet.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDeleted(DeviceId deviceId) {
|
||||||
|
parent.onDeviceDeleted(this.getSessionInfo().getDeviceName());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isAckExpected(MqttMessage message) {
|
private boolean isAckExpected(MqttMessage message) {
|
||||||
return message.fixedHeader().qosLevel().value() > 0;
|
return message.fixedHeader().qosLevel().value() > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,6 +178,10 @@ public class GatewaySessionHandler {
|
|||||||
devices.forEach(this::deregisterSession);
|
devices.forEach(this::deregisterSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onDeviceDeleted(String deviceName) {
|
||||||
|
deregisterSession(deviceName);
|
||||||
|
}
|
||||||
|
|
||||||
public String getNodeId() {
|
public String getNodeId() {
|
||||||
return context.getNodeId();
|
return context.getNodeId();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,8 @@ public interface SessionMsgListener {
|
|||||||
|
|
||||||
void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse);
|
void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse);
|
||||||
|
|
||||||
|
void onDeviceDeleted(DeviceId deviceId);
|
||||||
|
|
||||||
default void onUplinkNotification(UplinkNotificationMsg notificationMsg){};
|
default void onUplinkNotification(UplinkNotificationMsg notificationMsg){};
|
||||||
|
|
||||||
default void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto toTransportUpdateCredentials){}
|
default void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto toTransportUpdateCredentials){}
|
||||||
@ -54,8 +56,6 @@ public interface SessionMsgListener {
|
|||||||
default void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device,
|
default void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device,
|
||||||
Optional<DeviceProfile> deviceProfileOpt) {}
|
Optional<DeviceProfile> deviceProfileOpt) {}
|
||||||
|
|
||||||
default void onDeviceDeleted(DeviceId deviceId) {}
|
|
||||||
|
|
||||||
default void onResourceUpdate(TransportProtos.ResourceUpdateMsg resourceUpdateMsgOpt) {}
|
default void onResourceUpdate(TransportProtos.ResourceUpdateMsg resourceUpdateMsgOpt) {}
|
||||||
|
|
||||||
default void onResourceDelete(TransportProtos.ResourceDeleteMsg resourceUpdateMsgOpt) {}
|
default void onResourceDelete(TransportProtos.ResourceDeleteMsg resourceUpdateMsgOpt) {}
|
||||||
|
|||||||
@ -91,6 +91,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
|
|||||||
import org.thingsboard.server.queue.provider.TbTransportQueueFactory;
|
import org.thingsboard.server.queue.provider.TbTransportQueueFactory;
|
||||||
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
|
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
|
||||||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
import org.thingsboard.server.queue.util.AfterStartUp;
|
||||||
import org.thingsboard.server.queue.util.TbTransportComponent;
|
import org.thingsboard.server.queue.util.TbTransportComponent;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@ -227,6 +228,10 @@ public class DefaultTransportService implements TransportService {
|
|||||||
transportNotificationsConsumer.subscribe(Collections.singleton(tpi));
|
transportNotificationsConsumer.subscribe(Collections.singleton(tpi));
|
||||||
transportApiRequestTemplate.init();
|
transportApiRequestTemplate.init();
|
||||||
mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("transport-consumer"));
|
mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("transport-consumer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterStartUp
|
||||||
|
private void start() {
|
||||||
mainConsumerExecutor.execute(() -> {
|
mainConsumerExecutor.execute(() -> {
|
||||||
while (!stopped) {
|
while (!stopped) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -339,6 +339,21 @@ public class MqttClientTest extends AbstractContainerTest {
|
|||||||
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
|
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deviceDeletedClosingSession() throws Exception {
|
||||||
|
restClient.login("tenant@thingsboard.org", "tenant");
|
||||||
|
String deviceForDeletingTestName = "Device for deleting notification test";
|
||||||
|
Device device = createDevice(deviceForDeletingTestName);
|
||||||
|
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
|
||||||
|
|
||||||
|
MqttMessageListener listener = new MqttMessageListener();
|
||||||
|
MqttClient mqttClient = getMqttClient(deviceCredentials, listener);
|
||||||
|
|
||||||
|
restClient.deleteDevice(device.getId());
|
||||||
|
TimeUnit.SECONDS.sleep(3);
|
||||||
|
Assert.assertFalse(mqttClient.isConnected());
|
||||||
|
}
|
||||||
|
|
||||||
private RuleChainId createRootRuleChainForRpcResponse() throws Exception {
|
private RuleChainId createRootRuleChainForRpcResponse() throws Exception {
|
||||||
RuleChain newRuleChain = new RuleChain();
|
RuleChain newRuleChain = new RuleChain();
|
||||||
newRuleChain.setName("testRuleChain");
|
newRuleChain.setName("testRuleChain");
|
||||||
|
|||||||
@ -374,6 +374,14 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
|
|||||||
Assert.assertEquals(clientResponse.toString(), serverResponse.getBody());
|
Assert.assertEquals(clientResponse.toString(), serverResponse.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deviceCreationAfterDeleted() throws Exception {
|
||||||
|
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.createdDevice.getId());
|
||||||
|
Optional<Device> deletedDevice = restClient.getDeviceById(this.createdDevice.getId());
|
||||||
|
Assert.assertTrue(deletedDevice.isEmpty());
|
||||||
|
this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkAttribute(boolean client, String expectedValue) throws Exception {
|
private void checkAttribute(boolean client, String expectedValue) throws Exception {
|
||||||
JsonObject gatewayAttributesRequest = new JsonObject();
|
JsonObject gatewayAttributesRequest = new JsonObject();
|
||||||
int messageId = new Random().nextInt(100);
|
int messageId = new Random().nextInt(100);
|
||||||
|
|||||||
@ -134,6 +134,7 @@ import {
|
|||||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||||
import cssjs from '@core/css/css';
|
import cssjs from '@core/css/css';
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { IAliasController } from '@core/api/widget-api.models';
|
||||||
|
|
||||||
// @dynamic
|
// @dynamic
|
||||||
@Component({
|
@Component({
|
||||||
@ -179,6 +180,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
@Input()
|
@Input()
|
||||||
parentDashboard?: IDashboardComponent = null;
|
parentDashboard?: IDashboardComponent = null;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
parentAliasController?: IAliasController = null;
|
||||||
|
|
||||||
@ViewChild('dashboardContainer') dashboardContainer: ElementRef<HTMLElement>;
|
@ViewChild('dashboardContainer') dashboardContainer: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
prevDashboard: Dashboard;
|
prevDashboard: Dashboard;
|
||||||
@ -419,7 +423,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
this.readonly = this.embedded || (this.singlePageMode && !this.widgetEditMode && !this.route.snapshot.queryParamMap.get('edit'))
|
this.readonly = this.embedded || (this.singlePageMode && !this.widgetEditMode && !this.route.snapshot.queryParamMap.get('edit'))
|
||||||
|| this.forceFullscreen || this.isMobileApp || this.authUser.authority === Authority.CUSTOMER_USER;
|
|| this.forceFullscreen || this.isMobileApp || this.authUser.authority === Authority.CUSTOMER_USER;
|
||||||
|
|
||||||
this.dashboardCtx.aliasController = new AliasController(this.utils,
|
this.dashboardCtx.aliasController = this.parentAliasController ? this.parentAliasController : new AliasController(this.utils,
|
||||||
this.entityService,
|
this.entityService,
|
||||||
this.translate,
|
this.translate,
|
||||||
() => this.dashboardCtx.stateController,
|
() => this.dashboardCtx.stateController,
|
||||||
|
|||||||
@ -21,7 +21,8 @@
|
|||||||
[hideToolbar]="true"
|
[hideToolbar]="true"
|
||||||
[currentState]="currentState"
|
[currentState]="currentState"
|
||||||
[dashboard]="dashboard"
|
[dashboard]="dashboard"
|
||||||
[parentDashboard]="parentDashboard">
|
[parentDashboard]="parentDashboard"
|
||||||
|
[parentAliasController]="parentAliasController">
|
||||||
</tb-dashboard-page>
|
</tb-dashboard-page>
|
||||||
<div class="tb-absolute-fill tb-widget-error" *ngIf="!stateExists">
|
<div class="tb-absolute-fill tb-widget-error" *ngIf="!stateExists">
|
||||||
<span>{{ 'dashboard.non-existent-dashboard-state-error' | translate:{stateId} }}</span>
|
<span>{{ 'dashboard.non-existent-dashboard-state-error' | translate:{stateId} }}</span>
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { PageComponent } from '@shared/components/page.component';
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models';
|
import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models';
|
||||||
import { StateObject } from '@core/api/widget-api.models';
|
import { IAliasController, StateObject } from '@core/api/widget-api.models';
|
||||||
import { updateEntityParams, WidgetContext } from '@home/models/widget-component.models';
|
import { updateEntityParams, WidgetContext } from '@home/models/widget-component.models';
|
||||||
import { deepClone, isDefinedAndNotNull, isNotEmptyStr, objToBase64 } from '@core/utils';
|
import { deepClone, isDefinedAndNotNull, isNotEmptyStr, objToBase64 } from '@core/utils';
|
||||||
import { IDashboardComponent } from '@home/models/dashboard-component.models';
|
import { IDashboardComponent } from '@home/models/dashboard-component.models';
|
||||||
@ -63,6 +63,8 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On
|
|||||||
|
|
||||||
parentDashboard: IDashboardComponent;
|
parentDashboard: IDashboardComponent;
|
||||||
|
|
||||||
|
parentAliasController: IAliasController;
|
||||||
|
|
||||||
stateExists = true;
|
stateExists = true;
|
||||||
|
|
||||||
private stateSubscription: Subscription;
|
private stateSubscription: Subscription;
|
||||||
@ -92,6 +94,7 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On
|
|||||||
this.parentDashboard = this.ctx.parentDashboard ?
|
this.parentDashboard = this.ctx.parentDashboard ?
|
||||||
this.ctx.parentDashboard : this.ctx.dashboard;
|
this.ctx.parentDashboard : this.ctx.dashboard;
|
||||||
if (this.syncParentStateParams) {
|
if (this.syncParentStateParams) {
|
||||||
|
this.parentAliasController = this.parentDashboard.aliasController;
|
||||||
this.stateSubscription = this.ctx.stateController.dashboardCtrl.dashboardCtx.stateChanged.subscribe(() => {
|
this.stateSubscription = this.ctx.stateController.dashboardCtrl.dashboardCtx.stateChanged.subscribe(() => {
|
||||||
this.updateCurrentState();
|
this.updateCurrentState();
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
|
|||||||
@ -41,6 +41,22 @@ export function createTooltip(target: L.Layer,
|
|||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function disablePopup(target: L.Layer) {
|
||||||
|
if (target.isPopupOpen()) {
|
||||||
|
target.closePopup();
|
||||||
|
}
|
||||||
|
target.unbindPopup();
|
||||||
|
target.off('popupopen');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enablePopup(target: L.Layer, popup: L.Popup,
|
||||||
|
settings: MarkerSettings | PolylineSettings | PolygonSettings, datasource: Datasource) {
|
||||||
|
target.bindPopup(popup);
|
||||||
|
target.on('popupopen', () => {
|
||||||
|
bindPopupActions(popup, settings, datasource);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | PolylineSettings | PolygonSettings,
|
export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | PolylineSettings | PolygonSettings,
|
||||||
datasource: Datasource) {
|
datasource: Datasource) {
|
||||||
const actions = popup.getElement().getElementsByClassName('tb-custom-action');
|
const actions = popup.getElement().getElementsByClassName('tb-custom-action');
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import {
|
|||||||
MarkerSettings,
|
MarkerSettings,
|
||||||
UnitedMapSettings
|
UnitedMapSettings
|
||||||
} from './map-models';
|
} from './map-models';
|
||||||
import { bindPopupActions, createTooltip, } from './maps-utils';
|
import { bindPopupActions, createTooltip, disablePopup, enablePopup, } from './maps-utils';
|
||||||
import { aspectCache, fillPattern, parseWithTranslation, processPattern, safeExecute } from './common-maps-utils';
|
import { aspectCache, fillPattern, parseWithTranslation, processPattern, safeExecute } from './common-maps-utils';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import { isDefined, isDefinedAndNotNull } from '@core/utils';
|
import { isDefined, isDefinedAndNotNull } from '@core/utils';
|
||||||
@ -37,6 +37,7 @@ export class Marker {
|
|||||||
tooltip: L.Popup;
|
tooltip: L.Popup;
|
||||||
data: FormattedData;
|
data: FormattedData;
|
||||||
dataSources: FormattedData[];
|
dataSources: FormattedData[];
|
||||||
|
isDragging = false;
|
||||||
|
|
||||||
constructor(private map: LeafletMap, private location: L.LatLng, public settings: UnitedMapSettings,
|
constructor(private map: LeafletMap, private location: L.LatLng, public settings: UnitedMapSettings,
|
||||||
data?: FormattedData, dataSources?, onDragendListener?) {
|
data?: FormattedData, dataSources?, onDragendListener?) {
|
||||||
@ -64,6 +65,7 @@ export class Marker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.settings.markerClick) {
|
if (this.settings.markerClick) {
|
||||||
|
if (!this.isDragging) {
|
||||||
this.leafletMarker.on('click', (event: LeafletMouseEvent) => {
|
this.leafletMarker.on('click', (event: LeafletMouseEvent) => {
|
||||||
for (const action in this.settings.markerClick) {
|
for (const action in this.settings.markerClick) {
|
||||||
if (typeof (this.settings.markerClick[action]) === 'function') {
|
if (typeof (this.settings.markerClick[action]) === 'function') {
|
||||||
@ -72,9 +74,22 @@ export class Marker {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.draggableMarker && onDragendListener) {
|
if (settings.draggableMarker && onDragendListener) {
|
||||||
this.leafletMarker.on('pm:dragend', (e) => onDragendListener(e, this.data));
|
this.leafletMarker.on('pm:dragend', (e) => {
|
||||||
|
onDragendListener(e, this.data);
|
||||||
|
this.isDragging = false;
|
||||||
|
if (settings.showTooltip && settings.showTooltipAction === 'click') {
|
||||||
|
enablePopup(this.leafletMarker, this.tooltip, settings, this.data.$datasource);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.leafletMarker.on('pm:dragstart', (e) => {
|
||||||
|
this.isDragging = true;
|
||||||
|
if (settings.showTooltip && settings.showTooltipAction === 'click') {
|
||||||
|
disablePopup(this.leafletMarker);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user