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,7 +128,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
128 } 128 }
129 } 129 }
130 alarmsMap.clear(); 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 return this.alarms; 132 return this.alarms;
133 } 133 }
134 134
@@ -22,13 +22,15 @@ import { TranslateService } from '@ngx-translate/core'; @@ -22,13 +22,15 @@ import { TranslateService } from '@ngx-translate/core';
22 import { Subscription } from 'rxjs'; 22 import { Subscription } from 'rxjs';
23 import { BreakpointObserver } from '@angular/cdk/layout'; 23 import { BreakpointObserver } from '@angular/cdk/layout';
24 import { deepClone } from '@core/utils'; 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 import { 26 import {
27 FILTER_EDIT_PANEL_DATA, 27 FILTER_EDIT_PANEL_DATA,
28 FiltersEditPanelComponent, 28 FiltersEditPanelComponent,
29 FiltersEditPanelData 29 FiltersEditPanelData
30 } from '@home/components/filter/filters-edit-panel.component'; 30 } from '@home/components/filter/filters-edit-panel.component';
31 import { ComponentPortal, PortalInjector } from '@angular/cdk/portal'; 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 @Component({ 35 @Component({
34 selector: 'tb-filters-edit', 36 selector: 'tb-filters-edit',
@@ -65,7 +67,8 @@ export class FiltersEditComponent implements OnInit, OnDestroy { @@ -65,7 +67,8 @@ export class FiltersEditComponent implements OnInit, OnDestroy {
65 constructor(private translate: TranslateService, 67 constructor(private translate: TranslateService,
66 private overlay: Overlay, 68 private overlay: Overlay,
67 private breakpointObserver: BreakpointObserver, 69 private breakpointObserver: BreakpointObserver,
68 - private viewContainerRef: ViewContainerRef) { 70 + private viewContainerRef: ViewContainerRef,
  71 + private dialog: MatDialog) {
69 } 72 }
70 73
71 private setupAliasController(aliasController: IAliasController) { 74 private setupAliasController(aliasController: IAliasController) {
@@ -101,33 +104,53 @@ export class FiltersEditComponent implements OnInit, OnDestroy { @@ -101,33 +104,53 @@ export class FiltersEditComponent implements OnInit, OnDestroy {
101 if (this.disabled || !this.hasEditableFilters) { 104 if (this.disabled || !this.hasEditableFilters) {
102 return; 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 private _createFiltersEditPanelInjector(overlayRef: OverlayRef, data: FiltersEditPanelData): PortalInjector { 156 private _createFiltersEditPanelInjector(overlayRef: OverlayRef, data: FiltersEditPanelData): PortalInjector {
@@ -42,7 +42,7 @@ import { Polygon } from './polygon'; @@ -42,7 +42,7 @@ import { Polygon } from './polygon';
42 import { createTooltip, parseArray, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; 42 import { createTooltip, parseArray, safeExecute } from '@home/components/widget/lib/maps/maps-utils';
43 import { WidgetContext } from '@home/models/widget-component.models'; 43 import { WidgetContext } from '@home/models/widget-component.models';
44 import { DatasourceData } from '@shared/models/widget.models'; 44 import { DatasourceData } from '@shared/models/widget.models';
45 -import { deepClone } from '@core/utils'; 45 +import { deepClone, isDefinedAndNotNull } from '@core/utils';
46 46
47 export default abstract class LeafletMap { 47 export default abstract class LeafletMap {
48 48
@@ -249,10 +249,10 @@ export default abstract class LeafletMap { @@ -249,10 +249,10 @@ export default abstract class LeafletMap {
249 if (!expression) return null; 249 if (!expression) return null;
250 const lat = expression[this.options.latKeyName]; 250 const lat = expression[this.options.latKeyName];
251 const lng = expression[this.options.lngKeyName]; 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 convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { 258 convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] {
@@ -25,7 +25,7 @@ import { @@ -25,7 +25,7 @@ import {
25 } from './schemes'; 25 } from './schemes';
26 import { EntityType } from '@shared/models/entity-type.models'; 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 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 30 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
31 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 31 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
@@ -68,6 +68,7 @@ export type MapSettings = { @@ -68,6 +68,7 @@ export type MapSettings = {
68 removeOutsideVisibleBounds: boolean, 68 removeOutsideVisibleBounds: boolean,
69 useCustomProvider: boolean, 69 useCustomProvider: boolean,
70 customProviderTileUrl: string; 70 customProviderTileUrl: string;
  71 + mapPageSize: number;
71 } 72 }
72 73
73 export enum MapProviders { 74 export enum MapProviders {
@@ -267,7 +268,8 @@ export const defaultSettings: any = { @@ -267,7 +268,8 @@ export const defaultSettings: any = {
267 credentials: '', 268 credentials: '',
268 markerClusteringSetting: null, 269 markerClusteringSetting: null,
269 draggableMarker: false, 270 draggableMarker: false,
270 - fitMapBounds: true 271 + fitMapBounds: true,
  272 + mapPageSize: DEFAULT_MAP_PAGE_SIZE
271 }; 273 };
272 274
273 export const hereProviders = [ 275 export const hereProviders = [
@@ -81,7 +81,7 @@ export class MapWidgetController implements MapWidgetInterface { @@ -81,7 +81,7 @@ export class MapWidgetController implements MapWidgetInterface {
81 this.map.saveMarkerLocation = this.setMarkerLocation; 81 this.map.saveMarkerLocation = this.setMarkerLocation;
82 this.pageLink = { 82 this.pageLink = {
83 page: 0, 83 page: 0,
84 - pageSize: DEFAULT_MAP_PAGE_SIZE, 84 + pageSize: this.settings.mapPageSize,
85 textSearch: null, 85 textSearch: null,
86 dynamic: true 86 dynamic: true
87 }; 87 };
@@ -24,6 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; @@ -24,6 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
24 import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; 24 import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models';
25 import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; 25 import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
26 import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; 26 import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
  27 +import { isDefinedAndNotNull } from '@core/utils';
27 28
28 const maxZoom = 4;// ? 29 const maxZoom = 4;// ?
29 30
@@ -209,11 +210,15 @@ export class ImageMap extends LeafletMap { @@ -209,11 +210,15 @@ export class ImageMap extends LeafletMap {
209 } 210 }
210 211
211 convertPosition(expression): L.LatLng { 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 convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { 224 convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] {
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 /// limitations under the License. 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 export const googleMapSettingsSchema = 19 export const googleMapSettingsSchema =
18 { 20 {
19 schema: { 21 schema: {
@@ -198,10 +200,6 @@ export const openstreetMapSettingsSchema = @@ -198,10 +200,6 @@ export const openstreetMapSettingsSchema =
198 label: 'OpenStreetMap.Mapnik (Default)' 200 label: 'OpenStreetMap.Mapnik (Default)'
199 }, 201 },
200 { 202 {
201 - value: 'OpenStreetMap.BlackAndWhite',  
202 - label: 'OpenStreetMap.BlackAndWhite'  
203 - },  
204 - {  
205 value: 'OpenStreetMap.HOT', 203 value: 'OpenStreetMap.HOT',
206 label: 'OpenStreetMap.HOT' 204 label: 'OpenStreetMap.HOT'
207 }, 205 },
@@ -246,6 +244,11 @@ export const commonMapSettingsSchema = @@ -246,6 +244,11 @@ export const commonMapSettingsSchema =
246 type: 'boolean', 244 type: 'boolean',
247 default: false 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 defaultCenterPosition: { 252 defaultCenterPosition: {
250 title: 'Default map center position (0,0)', 253 title: 'Default map center position (0,0)',
251 type: 'string', 254 type: 'string',
@@ -408,6 +411,7 @@ export const commonMapSettingsSchema = @@ -408,6 +411,7 @@ export const commonMapSettingsSchema =
408 key: 'fitMapBounds', 411 key: 'fitMapBounds',
409 condition: 'model.provider !== "image-map"' 412 condition: 'model.provider !== "image-map"'
410 }, 413 },
  414 + 'mapPageSize',
411 'draggableMarker', 415 'draggableMarker',
412 { 416 {
413 key: 'disableScrollZooming', 417 key: 'disableScrollZooming',