Commit 257f1a7ce78c043a4f765f41629e9fb1e23034c4

Authored by Andrii Shvaika
2 parents ee4e0086 d151455f

Merge branch 'master' of github.com:thingsboard/thingsboard

... ... @@ -128,7 +128,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
128 128 }
129 129 }
130 130 alarmsMap.clear();
131   - alarmsMap.putAll(alarms.getData().stream().collect(Collectors.toMap(AlarmData::getId, Function.identity())));
  131 + alarmsMap.putAll(alarms.getData().stream().collect(Collectors.toMap(AlarmData::getId, Function.identity(), (a, b) -> a)));
132 132 return this.alarms;
133 133 }
134 134
... ...
... ... @@ -22,13 +22,15 @@ import { TranslateService } from '@ngx-translate/core';
22 22 import { Subscription } from 'rxjs';
23 23 import { BreakpointObserver } from '@angular/cdk/layout';
24 24 import { deepClone } from '@core/utils';
25   -import { FilterInfo, isFilterEditable } from '@shared/models/query/query.models';
  25 +import { Filter, FilterInfo, isFilterEditable } from '@shared/models/query/query.models';
26 26 import {
27 27 FILTER_EDIT_PANEL_DATA,
28 28 FiltersEditPanelComponent,
29 29 FiltersEditPanelData
30 30 } from '@home/components/filter/filters-edit-panel.component';
31 31 import { ComponentPortal, PortalInjector } from '@angular/cdk/portal';
  32 +import { UserFilterDialogComponent, UserFilterDialogData } from '@home/components/filter/user-filter-dialog.component';
  33 +import { MatDialog } from '@angular/material/dialog';
32 34
33 35 @Component({
34 36 selector: 'tb-filters-edit',
... ... @@ -65,7 +67,8 @@ export class FiltersEditComponent implements OnInit, OnDestroy {
65 67 constructor(private translate: TranslateService,
66 68 private overlay: Overlay,
67 69 private breakpointObserver: BreakpointObserver,
68   - private viewContainerRef: ViewContainerRef) {
  70 + private viewContainerRef: ViewContainerRef,
  71 + private dialog: MatDialog) {
69 72 }
70 73
71 74 private setupAliasController(aliasController: IAliasController) {
... ... @@ -101,33 +104,53 @@ export class FiltersEditComponent implements OnInit, OnDestroy {
101 104 if (this.disabled || !this.hasEditableFilters) {
102 105 return;
103 106 }
104   - const position = this.overlay.position();
105   - const config = new OverlayConfig({
106   - panelClass: 'tb-filters-edit-panel',
107   - backdropClass: 'cdk-overlay-transparent-backdrop',
108   - hasBackdrop: true,
109   - });
110   - const connectedPosition: ConnectedPosition = {
111   - originX: 'start',
112   - originY: 'bottom',
113   - overlayX: 'start',
114   - overlayY: 'top'
115   - };
116   - config.positionStrategy = position.flexibleConnectedTo(this.filtersEditPanelOrigin.elementRef)
117   - .withPositions([connectedPosition]);
118   - const overlayRef = this.overlay.create(config);
119   - overlayRef.backdropClick().subscribe(() => {
120   - overlayRef.dispose();
121   - });
122   -
123   - const injector = this._createFiltersEditPanelInjector(
124   - overlayRef,
125   - {
126   - aliasController: this.aliasController,
127   - filtersInfo: deepClone(this.filtersInfo)
128   - }
129   - );
130   - overlayRef.attach(new ComponentPortal(FiltersEditPanelComponent, this.viewContainerRef, injector));
  107 + const filteredArray = Object.entries(this.filtersInfo);
  108 +
  109 + if (filteredArray.length === 1) {
  110 + const singleFilter: Filter = {id: filteredArray[0][0], ...filteredArray[0][1]};
  111 + this.dialog.open<UserFilterDialogComponent, UserFilterDialogData,
  112 + Filter>(UserFilterDialogComponent, {
  113 + disableClose: true,
  114 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  115 + data: {
  116 + filter: singleFilter
  117 + }
  118 + }).afterClosed().subscribe(
  119 + (result) => {
  120 + if (result) {
  121 + this.filtersInfo[result.id] = result;
  122 + this.aliasController.updateUserFilter(result);
  123 + }
  124 + });
  125 + } else {
  126 + const position = this.overlay.position();
  127 + const config = new OverlayConfig({
  128 + panelClass: 'tb-filters-edit-panel',
  129 + backdropClass: 'cdk-overlay-transparent-backdrop',
  130 + hasBackdrop: true,
  131 + });
  132 + const connectedPosition: ConnectedPosition = {
  133 + originX: 'start',
  134 + originY: 'bottom',
  135 + overlayX: 'start',
  136 + overlayY: 'top'
  137 + };
  138 + config.positionStrategy = position.flexibleConnectedTo(this.filtersEditPanelOrigin.elementRef)
  139 + .withPositions([connectedPosition]);
  140 + const overlayRef = this.overlay.create(config);
  141 + overlayRef.backdropClick().subscribe(() => {
  142 + overlayRef.dispose();
  143 + });
  144 +
  145 + const injector = this._createFiltersEditPanelInjector(
  146 + overlayRef,
  147 + {
  148 + aliasController: this.aliasController,
  149 + filtersInfo: deepClone(this.filtersInfo)
  150 + }
  151 + );
  152 + overlayRef.attach(new ComponentPortal(FiltersEditPanelComponent, this.viewContainerRef, injector));
  153 + }
131 154 }
132 155
133 156 private _createFiltersEditPanelInjector(overlayRef: OverlayRef, data: FiltersEditPanelData): PortalInjector {
... ...
... ... @@ -42,7 +42,7 @@ import { Polygon } from './polygon';
42 42 import { createTooltip, parseArray, safeExecute } from '@home/components/widget/lib/maps/maps-utils';
43 43 import { WidgetContext } from '@home/models/widget-component.models';
44 44 import { DatasourceData } from '@shared/models/widget.models';
45   -import { deepClone } from '@core/utils';
  45 +import { deepClone, isDefinedAndNotNull } from '@core/utils';
46 46
47 47 export default abstract class LeafletMap {
48 48
... ... @@ -249,10 +249,10 @@ export default abstract class LeafletMap {
249 249 if (!expression) return null;
250 250 const lat = expression[this.options.latKeyName];
251 251 const lng = expression[this.options.lngKeyName];
252   - if (isNaN(lat) || isNaN(lng))
253   - return null;
254   - else
255   - return L.latLng(lat, lng) as L.LatLng;
  252 + if (!isDefinedAndNotNull(lat) || isNaN(lat) || !isDefinedAndNotNull(lng) || isNaN(lng)) {
  253 + return null;
  254 + }
  255 + return L.latLng(lat, lng) as L.LatLng;
256 256 }
257 257
258 258 convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] {
... ...
... ... @@ -25,7 +25,7 @@ import {
25 25 } from './schemes';
26 26 import { EntityType } from '@shared/models/entity-type.models';
27 27
28   -export const DEFAULT_MAP_PAGE_SIZE = 1024;
  28 +export const DEFAULT_MAP_PAGE_SIZE = 16384;
29 29
30 30 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
31 31 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
... ... @@ -68,6 +68,7 @@ export type MapSettings = {
68 68 removeOutsideVisibleBounds: boolean,
69 69 useCustomProvider: boolean,
70 70 customProviderTileUrl: string;
  71 + mapPageSize: number;
71 72 }
72 73
73 74 export enum MapProviders {
... ... @@ -267,7 +268,8 @@ export const defaultSettings: any = {
267 268 credentials: '',
268 269 markerClusteringSetting: null,
269 270 draggableMarker: false,
270   - fitMapBounds: true
  271 + fitMapBounds: true,
  272 + mapPageSize: DEFAULT_MAP_PAGE_SIZE
271 273 };
272 274
273 275 export const hereProviders = [
... ...
... ... @@ -81,7 +81,7 @@ export class MapWidgetController implements MapWidgetInterface {
81 81 this.map.saveMarkerLocation = this.setMarkerLocation;
82 82 this.pageLink = {
83 83 page: 0,
84   - pageSize: DEFAULT_MAP_PAGE_SIZE,
  84 + pageSize: this.settings.mapPageSize,
85 85 textSearch: null,
86 86 dynamic: true
87 87 };
... ...
... ... @@ -24,6 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
24 24 import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models';
25 25 import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
26 26 import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
  27 +import { isDefinedAndNotNull } from '@core/utils';
27 28
28 29 const maxZoom = 4;// ?
29 30
... ... @@ -209,11 +210,15 @@ export class ImageMap extends LeafletMap {
209 210 }
210 211
211 212 convertPosition(expression): L.LatLng {
212   - if (isNaN(expression[this.options.xPosKeyName]) || isNaN(expression[this.options.yPosKeyName])) return null;
213   - Object.assign(expression, this.posFunction(expression[this.options.xPosKeyName], expression[this.options.yPosKeyName]));
214   - return this.pointToLatLng(
215   - expression.x * this.width,
216   - expression.y * this.height);
  213 + const xPos = expression[this.options.xPosKeyName];
  214 + const yPos = expression[this.options.yPosKeyName];
  215 + if (!isDefinedAndNotNull(xPos) || isNaN(xPos) || !isDefinedAndNotNull(yPos) || isNaN(yPos)) {
  216 + return null;
  217 + }
  218 + Object.assign(expression, this.posFunction(xPos, yPos));
  219 + return this.pointToLatLng(
  220 + expression.x * this.width,
  221 + expression.y * this.height);
217 222 }
218 223
219 224 convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] {
... ...
... ... @@ -14,6 +14,8 @@
14 14 /// limitations under the License.
15 15 ///
16 16
  17 +import { DEFAULT_MAP_PAGE_SIZE } from '@home/components/widget/lib/maps/map-models';
  18 +
17 19 export const googleMapSettingsSchema =
18 20 {
19 21 schema: {
... ... @@ -198,10 +200,6 @@ export const openstreetMapSettingsSchema =
198 200 label: 'OpenStreetMap.Mapnik (Default)'
199 201 },
200 202 {
201   - value: 'OpenStreetMap.BlackAndWhite',
202   - label: 'OpenStreetMap.BlackAndWhite'
203   - },
204   - {
205 203 value: 'OpenStreetMap.HOT',
206 204 label: 'OpenStreetMap.HOT'
207 205 },
... ... @@ -246,6 +244,11 @@ export const commonMapSettingsSchema =
246 244 type: 'boolean',
247 245 default: false
248 246 },
  247 + mapPageSize: {
  248 + title: 'Map page size load entities',
  249 + type: 'number',
  250 + default: DEFAULT_MAP_PAGE_SIZE
  251 + },
249 252 defaultCenterPosition: {
250 253 title: 'Default map center position (0,0)',
251 254 type: 'string',
... ... @@ -408,6 +411,7 @@ export const commonMapSettingsSchema =
408 411 key: 'fitMapBounds',
409 412 condition: 'model.provider !== "image-map"'
410 413 },
  414 + 'mapPageSize',
411 415 'draggableMarker',
412 416 {
413 417 key: 'disableScrollZooming',
... ...