Commit 10ddb5f8f37581611df15cb154127ce1f4d683bd

Authored by Igor Kulikov
1 parent 67dd34db

Fix timeseries widget (invoke data updated callback from data aggregator on init…

…ial data). Improve widget selector.
@@ -155,6 +155,7 @@ export class DataAggregator { @@ -155,6 +155,7 @@ export class DataAggregator {
155 } 155 }
156 156
157 public onData(data: SubscriptionDataHolder, update: boolean, history: boolean, detectChanges: boolean) { 157 public onData(data: SubscriptionDataHolder, update: boolean, history: boolean, detectChanges: boolean) {
  158 + this.updatedData = true;
158 if (!this.dataReceived || this.resetPending) { 159 if (!this.dataReceived || this.resetPending) {
159 let updateIntervalScheduledTime = true; 160 let updateIntervalScheduledTime = true;
160 if (!this.dataReceived) { 161 if (!this.dataReceived) {
@@ -183,7 +184,6 @@ export class DataAggregator { @@ -183,7 +184,6 @@ export class DataAggregator {
183 this.onInterval(history, detectChanges); 184 this.onInterval(history, detectChanges);
184 } 185 }
185 } 186 }
186 - this.updatedData = true;  
187 } 187 }
188 188
189 private onInterval(history?: boolean, detectChanges?: boolean) { 189 private onInterval(history?: boolean, detectChanges?: boolean) {
@@ -55,6 +55,8 @@ export class WidgetService { @@ -55,6 +55,8 @@ export class WidgetService {
55 private systemWidgetsBundles: Array<WidgetsBundle>; 55 private systemWidgetsBundles: Array<WidgetsBundle>;
56 private tenantWidgetsBundles: Array<WidgetsBundle>; 56 private tenantWidgetsBundles: Array<WidgetsBundle>;
57 57
  58 + private widgetTypeInfosCache = new Map<string, Array<WidgetTypeInfo>>();
  59 +
58 private loadWidgetsBundleCacheSubject: ReplaySubject<any>; 60 private loadWidgetsBundleCacheSubject: ReplaySubject<any>;
59 61
60 constructor( 62 constructor(
@@ -137,8 +139,15 @@ export class WidgetService { @@ -137,8 +139,15 @@ export class WidgetService {
137 139
138 public getBundleWidgetTypeInfos(bundleAlias: string, isSystem: boolean, 140 public getBundleWidgetTypeInfos(bundleAlias: string, isSystem: boolean,
139 config?: RequestConfig): Observable<Array<WidgetTypeInfo>> { 141 config?: RequestConfig): Observable<Array<WidgetTypeInfo>> {
140 - return this.http.get<Array<WidgetTypeInfo>>(`/api/widgetTypesInfos?isSystem=${isSystem}&bundleAlias=${bundleAlias}`,  
141 - defaultHttpOptionsFromConfig(config)); 142 + const key = bundleAlias + (isSystem ? '_sys' : '');
  143 + if (this.widgetTypeInfosCache.has(key)) {
  144 + return of(this.widgetTypeInfosCache.get(key));
  145 + } else {
  146 + return this.http.get<Array<WidgetTypeInfo>>(`/api/widgetTypesInfos?isSystem=${isSystem}&bundleAlias=${bundleAlias}`,
  147 + defaultHttpOptionsFromConfig(config)).pipe(
  148 + tap((res) => this.widgetTypeInfosCache.set(key, res) )
  149 + );
  150 + }
142 } 151 }
143 152
144 public loadBundleLibraryWidgets(bundleAlias: string, isSystem: boolean, 153 public loadBundleLibraryWidgets(bundleAlias: string, isSystem: boolean,
@@ -305,6 +314,7 @@ export class WidgetService { @@ -305,6 +314,7 @@ export class WidgetService {
305 this.systemWidgetsBundles = undefined; 314 this.systemWidgetsBundles = undefined;
306 this.tenantWidgetsBundles = undefined; 315 this.tenantWidgetsBundles = undefined;
307 this.loadWidgetsBundleCacheSubject = undefined; 316 this.loadWidgetsBundleCacheSubject = undefined;
  317 + this.widgetTypeInfosCache.clear();
308 } 318 }
309 319
310 } 320 }
@@ -242,8 +242,9 @@ @@ -242,8 +242,9 @@
242 </tb-edit-widget> 242 </tb-edit-widget>
243 </tb-details-panel> 243 </tb-details-panel>
244 <tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" fxFlex 244 <tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" fxFlex
245 - headerTitle="{{  
246 - (!widgetsBundle?.title ? 'widget.select-widgets-bundle' : 'dashboard.select-widget-value') | translate: widgetsBundle 245 + headerTitle="{{ isAddingWidget ?
  246 + ((!dashboardWidgetSelectComponent?.widgetsBundle ?
  247 + 'widget.select-widgets-bundle' : 'dashboard.select-widget-value') | translate: dashboardWidgetSelectComponent?.widgetsBundle) : ''
247 }}" 248 }}"
248 headerHeightPx="64" 249 headerHeightPx="64"
249 [isShowSearch]="true" 250 [isShowSearch]="true"
@@ -252,34 +253,33 @@ @@ -252,34 +253,33 @@
252 backgroundColor="#cfd8dc" 253 backgroundColor="#cfd8dc"
253 (closeDetails)="onAddWidgetClosed()" 254 (closeDetails)="onAddWidgetClosed()"
254 (closeSearch)="onCloseSearchBundle()"> 255 (closeSearch)="onCloseSearchBundle()">
255 - <div class="prefix-title-buttons" [fxHide]="!widgetsBundle?.title" style="height: 28px; margin-right: 12px">  
256 - <button class="tb-mat-28" mat-icon-button type="button" (click)="widgetBundleSelected(null)"> 256 + <div class="prefix-title-buttons" [fxShow]="(isAddingWidget && dashboardWidgetSelectComponent?.widgetsBundle) ? true : false" style="height: 28px; margin-right: 12px">
  257 + <button class="tb-mat-28" mat-icon-button type="button" (click)="clearSelectedWidgetBundle()">
257 <mat-icon>arrow_back</mat-icon> 258 <mat-icon>arrow_back</mat-icon>
258 </button> 259 </button>
259 </div> 260 </div>
260 <div class="search-pane" *ngIf="isAddingWidget" fxLayout="row"> 261 <div class="search-pane" *ngIf="isAddingWidget" fxLayout="row">
261 <tb-widgets-bundle-search fxFlex 262 <tb-widgets-bundle-search fxFlex
262 [(ngModel)]="searchBundle" 263 [(ngModel)]="searchBundle"
263 - placeholder="{{ (!widgetsBundle?.title ? 'widgets-bundle.search' : 'widget.search') | translate }}" 264 + placeholder="{{ (!dashboardWidgetSelectComponent?.widgetsBundle ? 'widgets-bundle.search' : 'widget.search') | translate }}"
264 (ngModelChange)="searchBundle = $event"> 265 (ngModelChange)="searchBundle = $event">
265 </tb-widgets-bundle-search> 266 </tb-widgets-bundle-search>
266 </div> 267 </div>
267 <div class="details-buttons" *ngIf="isAddingWidget"> 268 <div class="details-buttons" *ngIf="isAddingWidget">
268 <button mat-button mat-icon-button type="button" 269 <button mat-button mat-icon-button type="button"
269 - *ngIf="widgetTypes.length > 1" 270 + *ngIf="dashboardWidgetSelectComponent?.widgetTypes.size > 1"
270 (click)="editWidgetsTypesToDisplay($event)" 271 (click)="editWidgetsTypesToDisplay($event)"
271 matTooltip="{{ 'widget.filter' | translate }}" 272 matTooltip="{{ 'widget.filter' | translate }}"
272 matTooltipPosition="above"> 273 matTooltipPosition="above">
273 <mat-icon>filter_list</mat-icon> 274 <mat-icon>filter_list</mat-icon>
274 </button> 275 </button>
275 </div> 276 </div>
276 - <tb-dashboard-widget-select *ngIf="isAddingWidget" 277 + <tb-dashboard-widget-select #dashboardWidgetSelect
  278 + *ngIf="isAddingWidget"
277 [aliasController]="dashboardCtx.aliasController" 279 [aliasController]="dashboardCtx.aliasController"
278 - [widgetsBundle]="widgetsBundle"  
279 [searchBundle]="searchBundle" 280 [searchBundle]="searchBundle"
280 [filterWidgetTypes]="filterWidgetTypes" 281 [filterWidgetTypes]="filterWidgetTypes"
281 - (widgetsTypes)="updateWidgetsTypes($event)"  
282 - (widgetsBundleSelected)="widgetBundleSelected($event)" 282 + (widgetsBundleSelected)="widgetBundleSelected()"
283 (widgetSelected)="addWidgetFromType($event)"> 283 (widgetSelected)="addWidgetFromType($event)">
284 </tb-dashboard-widget-select> 284 </tb-dashboard-widget-select>
285 </tb-details-panel> 285 </tb-details-panel>
@@ -121,6 +121,7 @@ import { @@ -121,6 +121,7 @@ import {
121 DisplayWidgetTypesPanelData, 121 DisplayWidgetTypesPanelData,
122 WidgetTypes 122 WidgetTypes
123 } from '@home/components/dashboard-page/widget-types-panel.component'; 123 } from '@home/components/dashboard-page/widget-types-panel.component';
  124 +import { DashboardWidgetSelectComponent } from '@home/components/dashboard-page/dashboard-widget-select.component';
124 125
125 // @dynamic 126 // @dynamic
126 @Component({ 127 @Component({
@@ -167,9 +168,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -167,9 +168,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
167 forceDashboardMobileMode = false; 168 forceDashboardMobileMode = false;
168 isAddingWidget = false; 169 isAddingWidget = false;
169 isAddingWidgetClosed = true; 170 isAddingWidgetClosed = true;
170 - widgetsBundle: WidgetsBundle = null;  
171 searchBundle = ''; 171 searchBundle = '';
172 - widgetTypes: WidgetTypes[] = [];  
173 filterWidgetTypes: widgetType[] = null; 172 filterWidgetTypes: widgetType[] = null;
174 173
175 isToolbarOpened = false; 174 isToolbarOpened = false;
@@ -267,6 +266,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -267,6 +266,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
267 266
268 @ViewChild('tbEditWidget') editWidgetComponent: EditWidgetComponent; 267 @ViewChild('tbEditWidget') editWidgetComponent: EditWidgetComponent;
269 268
  269 + @ViewChild('dashboardWidgetSelect') dashboardWidgetSelectComponent: DashboardWidgetSelectComponent;
  270 +
270 constructor(protected store: Store<AppState>, 271 constructor(protected store: Store<AppState>,
271 @Inject(WINDOW) private window: Window, 272 @Inject(WINDOW) private window: Window,
272 private breakpointObserver: BreakpointObserver, 273 private breakpointObserver: BreakpointObserver,
@@ -367,7 +368,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -367,7 +368,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
367 this.forceDashboardMobileMode = false; 368 this.forceDashboardMobileMode = false;
368 this.isAddingWidget = false; 369 this.isAddingWidget = false;
369 this.isAddingWidgetClosed = true; 370 this.isAddingWidgetClosed = true;
370 - this.widgetsBundle = null;  
371 371
372 this.isToolbarOpened = false; 372 this.isToolbarOpened = false;
373 this.isToolbarOpenedAnimate = false; 373 this.isToolbarOpenedAnimate = false;
@@ -885,7 +885,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -885,7 +885,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
885 885
886 addWidgetFromType(widget: WidgetInfo) { 886 addWidgetFromType(widget: WidgetInfo) {
887 this.onAddWidgetClosed(); 887 this.onAddWidgetClosed();
888 - this.widgetTypes = [];  
889 this.searchBundle = ''; 888 this.searchBundle = '';
890 this.widgetComponentService.getWidgetInfo(widget.bundleAlias, widget.typeAlias, widget.isSystemType).subscribe( 889 this.widgetComponentService.getWidgetInfo(widget.bundleAlias, widget.typeAlias, widget.isSystemType).subscribe(
891 (widgetTypeInfo) => { 890 (widgetTypeInfo) => {
@@ -1153,16 +1152,13 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -1153,16 +1152,13 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
1153 return widgetContextActions; 1152 return widgetContextActions;
1154 } 1153 }
1155 1154
1156 - widgetBundleSelected(bundle: WidgetsBundle){  
1157 - this.widgetsBundle = bundle;  
1158 - this.widgetTypes = []; 1155 + widgetBundleSelected(){
1159 this.searchBundle = ''; 1156 this.searchBundle = '';
1160 } 1157 }
1161 1158
1162 - updateWidgetsTypes(types: Set<widgetType>) {  
1163 - this.widgetTypes = Array.from(types.values()).map(type => {  
1164 - return {type, display: true};  
1165 - }); 1159 + clearSelectedWidgetBundle() {
  1160 + this.searchBundle = '';
  1161 + this.dashboardWidgetSelectComponent.widgetsBundle = null;
1166 } 1162 }
1167 1163
1168 editWidgetsTypesToDisplay($event: Event) { 1164 editWidgetsTypesToDisplay($event: Event) {
@@ -1191,7 +1187,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -1191,7 +1187,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
1191 { 1187 {
1192 provide: DISPLAY_WIDGET_TYPES_PANEL_DATA, 1188 provide: DISPLAY_WIDGET_TYPES_PANEL_DATA,
1193 useValue: { 1189 useValue: {
1194 - types: this.widgetTypes, 1190 + types: Array.from(this.dashboardWidgetSelectComponent.widgetTypes.values()).map(type => {
  1191 + return {type, display: true};
  1192 + }),
1195 typesUpdated: (newTypes) => { 1193 typesUpdated: (newTypes) => {
1196 this.filterWidgetTypes = newTypes.filter(type => type.display).map(type => type.type); 1194 this.filterWidgetTypes = newTypes.filter(type => type.display).map(type => type.type);
1197 } 1195 }
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 --> 17 -->
18 <div class="widget-select"> 18 <div class="widget-select">
19 <div *ngIf="widgetsBundle; else bundles"> 19 <div *ngIf="widgetsBundle; else bundles">
20 - <div *ngIf="(widgets$ | async)?.length; else emptyBundle" fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap"> 20 + <div *ngIf="(widgets$ | async)?.length; else loadingWidgets" fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap">
21 <div *ngFor="let widget of widgets$ | async" class="mat-card-container"> 21 <div *ngFor="let widget of widgets$ | async" class="mat-card-container">
22 <mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="onWidgetClicked($event, widget)"> 22 <mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="onWidgetClicked($event, widget)">
23 <div class="preview-container" fxFlex="45"> 23 <div class="preview-container" fxFlex="45">
@@ -33,15 +33,24 @@ @@ -33,15 +33,24 @@
33 </mat-card> 33 </mat-card>
34 </div> 34 </div>
35 </div> 35 </div>
36 - <ng-template #emptyBundle>  
37 - <span translate  
38 - style="display: flex;"  
39 - fxLayoutAlign="center center"  
40 - class="mat-headline tb-absolute-fill">widgets-bundle.empty</span> 36 + <ng-template #loadingWidgets>
  37 + <div *ngIf="loadingWidgets$ | async; else emptyBundle" fxLayout="column"
  38 + fxLayoutAlign="center center" class="tb-absolute-fill">
  39 + <span class="mat-headline" style="padding-bottom: 20px;">
  40 + {{ 'widget.loading-widgets' | translate }}
  41 + </span>
  42 + <mat-spinner color="accent" strokeWidth="5"></mat-spinner>
  43 + </div>
  44 + <ng-template #emptyBundle>
  45 + <span translate
  46 + style="display: flex;"
  47 + fxLayoutAlign="center center"
  48 + class="mat-headline tb-absolute-fill">widgets-bundle.empty</span>
  49 + </ng-template>
41 </ng-template> 50 </ng-template>
42 </div> 51 </div>
43 <ng-template #bundles> 52 <ng-template #bundles>
44 - <div fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap"> 53 + <div *ngIf="(widgetsBundles$ | async)?.length; else loadingWidgetBundles" fxFlexFill fxLayoutGap="12px grid" fxLayout="row wrap">
45 <div *ngFor="let widgetsBundle of widgetsBundles$ | async" class="mat-card-container"> 54 <div *ngFor="let widgetsBundle of widgetsBundles$ | async" class="mat-card-container">
46 <mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="selectBundle($event, widgetsBundle)"> 55 <mat-card fxFlexFill fxLayout="row" fxLayoutGap="16px" (click)="selectBundle($event, widgetsBundle)">
47 <div class="preview-container" fxFlex="45"> 56 <div class="preview-container" fxFlex="45">
@@ -57,5 +66,20 @@ @@ -57,5 +66,20 @@
57 </mat-card> 66 </mat-card>
58 </div> 67 </div>
59 </div> 68 </div>
  69 + <ng-template #loadingWidgetBundles>
  70 + <div *ngIf="loadingWidgetBundles$ | async; else noWidgetBundles" fxLayout="column"
  71 + fxLayoutAlign="center center" class="tb-absolute-fill">
  72 + <span class="mat-headline" style="padding-bottom: 20px;">
  73 + {{ 'widgets-bundle.loading-widgets-bundles' | translate }}
  74 + </span>
  75 + <mat-spinner strokeWidth="5"></mat-spinner>
  76 + </div>
  77 + <ng-template #noWidgetBundles>
  78 + <span translate
  79 + style="display: flex;"
  80 + fxLayoutAlign="center center"
  81 + class="mat-headline tb-absolute-fill">widgets-bundle.no-widgets-bundles-text</span>
  82 + </ng-template>
  83 + </ng-template>
60 </ng-template> 84 </ng-template>
61 </div> 85 </div>
@@ -29,8 +29,15 @@ @@ -29,8 +29,15 @@
29 flex: 0 0 100%; 29 flex: 0 0 100%;
30 max-width: 100%; 30 max-width: 100%;
31 31
  32 + &:hover {
  33 + .mat-card {
  34 + box-shadow: 0 2px 6px 6px rgb(0 0 0 / 20%), 0 1px 4px 2px rgb(0 0 0 / 14%), 0 1px 6px 0 rgb(0 0 0 / 12%)
  35 + }
  36 + }
  37 +
32 .mat-card { 38 .mat-card {
33 cursor: pointer; 39 cursor: pointer;
  40 + transition: box-shadow 0.2s;
34 41
35 .preview-container { 42 .preview-container {
36 text-align: center; 43 text-align: center;
@@ -20,8 +20,7 @@ import { IAliasController } from '@core/api/widget-api.models'; @@ -20,8 +20,7 @@ import { IAliasController } from '@core/api/widget-api.models';
20 import { NULL_UUID } from '@shared/models/id/has-uuid'; 20 import { NULL_UUID } from '@shared/models/id/has-uuid';
21 import { WidgetService } from '@core/http/widget.service'; 21 import { WidgetService } from '@core/http/widget.service';
22 import { WidgetInfo, widgetType } from '@shared/models/widget.models'; 22 import { WidgetInfo, widgetType } from '@shared/models/widget.models';
23 -import { toWidgetInfo } from '@home/models/widget-component.models';  
24 -import { distinctUntilChanged, map, publishReplay, refCount, switchMap, tap } from 'rxjs/operators'; 23 +import { distinctUntilChanged, map, publishReplay, refCount, share, switchMap, tap } from 'rxjs/operators';
25 import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; 24 import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
26 import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; 25 import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
27 import { isDefinedAndNotNull } from '@core/utils'; 26 import { isDefinedAndNotNull } from '@core/utils';
@@ -37,18 +36,28 @@ export class DashboardWidgetSelectComponent implements OnInit { @@ -37,18 +36,28 @@ export class DashboardWidgetSelectComponent implements OnInit {
37 private filterWidgetTypes$ = new BehaviorSubject<Array<widgetType>>(null); 36 private filterWidgetTypes$ = new BehaviorSubject<Array<widgetType>>(null);
38 private widgetsInfo: Observable<Array<WidgetInfo>>; 37 private widgetsInfo: Observable<Array<WidgetInfo>>;
39 private widgetsBundleValue: WidgetsBundle; 38 private widgetsBundleValue: WidgetsBundle;
40 - private widgetsType = new Set<widgetType>(); 39 + widgetTypes = new Set<widgetType>();
41 40
42 widgets$: Observable<Array<WidgetInfo>>; 41 widgets$: Observable<Array<WidgetInfo>>;
  42 + loadingWidgetsSubject: BehaviorSubject<boolean> = new BehaviorSubject(false);
  43 + loadingWidgets$ = this.loadingWidgetsSubject.pipe(
  44 + share()
  45 + );
43 widgetsBundles$: Observable<Array<WidgetsBundle>>; 46 widgetsBundles$: Observable<Array<WidgetsBundle>>;
  47 + loadingWidgetBundlesSubject: BehaviorSubject<boolean> = new BehaviorSubject(true);
  48 + loadingWidgetBundles$ = this.loadingWidgetBundlesSubject.pipe(
  49 + share()
  50 + );
44 51
45 - @Input()  
46 set widgetsBundle(widgetBundle: WidgetsBundle) { 52 set widgetsBundle(widgetBundle: WidgetsBundle) {
47 - this.widgetsInfo = null;  
48 - this.widgetsType.clear();  
49 - this.widgetsTypes.emit(this.widgetsType);  
50 - this.filterWidgetTypes$.next(null);  
51 - this.widgetsBundleValue = widgetBundle; 53 + if (this.widgetsBundleValue !== widgetBundle) {
  54 + this.widgetsBundleValue = widgetBundle;
  55 + if (widgetBundle === null) {
  56 + this.widgetTypes.clear();
  57 + }
  58 + this.filterWidgetTypes$.next(null);
  59 + this.widgetsInfo = null;
  60 + }
52 } 61 }
53 62
54 get widgetsBundle(): WidgetsBundle { 63 get widgetsBundle(): WidgetsBundle {
@@ -74,14 +83,8 @@ export class DashboardWidgetSelectComponent implements OnInit { @@ -74,14 +83,8 @@ export class DashboardWidgetSelectComponent implements OnInit {
74 @Output() 83 @Output()
75 widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>(); 84 widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>();
76 85
77 - @Output()  
78 - widgetsTypes: EventEmitter<Set<widgetType>> = new EventEmitter<Set<widgetType>>();  
79 -  
80 constructor(private widgetsService: WidgetService, 86 constructor(private widgetsService: WidgetService,
81 private sanitizer: DomSanitizer) { 87 private sanitizer: DomSanitizer) {
82 - }  
83 -  
84 - ngOnInit(): void {  
85 this.widgetsBundles$ = this.search$.asObservable().pipe( 88 this.widgetsBundles$ = this.search$.asObservable().pipe(
86 distinctUntilChanged(), 89 distinctUntilChanged(),
87 switchMap(search => this.fetchWidgetBundle(search)) 90 switchMap(search => this.fetchWidgetBundle(search))
@@ -92,27 +95,41 @@ export class DashboardWidgetSelectComponent implements OnInit { @@ -92,27 +95,41 @@ export class DashboardWidgetSelectComponent implements OnInit {
92 ); 95 );
93 } 96 }
94 97
  98 + ngOnInit(): void {
  99 + }
  100 +
95 private getWidgets(): Observable<Array<WidgetInfo>> { 101 private getWidgets(): Observable<Array<WidgetInfo>> {
96 if (!this.widgetsInfo) { 102 if (!this.widgetsInfo) {
97 if (this.widgetsBundle !== null) { 103 if (this.widgetsBundle !== null) {
98 const bundleAlias = this.widgetsBundle.alias; 104 const bundleAlias = this.widgetsBundle.alias;
99 const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; 105 const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID;
  106 + this.loadingWidgetsSubject.next(true);
100 this.widgetsInfo = this.widgetsService.getBundleWidgetTypeInfos(bundleAlias, isSystem).pipe( 107 this.widgetsInfo = this.widgetsService.getBundleWidgetTypeInfos(bundleAlias, isSystem).pipe(
101 - map(widgets => widgets.sort((a, b) => b.createdTime - a.createdTime)),  
102 - map(widgets => widgets.map((widgetTypeInfo) => {  
103 - this.widgetsType.add(widgetTypeInfo.widgetType);  
104 - const widget: WidgetInfo = {  
105 - isSystemType: isSystem,  
106 - bundleAlias,  
107 - typeAlias: widgetTypeInfo.alias,  
108 - type: widgetTypeInfo.widgetType,  
109 - title: widgetTypeInfo.name,  
110 - image: widgetTypeInfo.image,  
111 - description: widgetTypeInfo.description  
112 - };  
113 - return widget;  
114 - })),  
115 - tap(() => this.widgetsTypes.emit(this.widgetsType)), 108 + map(widgets => {
  109 + widgets = widgets.sort((a, b) => b.createdTime - a.createdTime);
  110 + const widgetTypes = new Set<widgetType>();
  111 + const widgetInfos = widgets.map((widgetTypeInfo) => {
  112 + widgetTypes.add(widgetTypeInfo.widgetType);
  113 + const widget: WidgetInfo = {
  114 + isSystemType: isSystem,
  115 + bundleAlias,
  116 + typeAlias: widgetTypeInfo.alias,
  117 + type: widgetTypeInfo.widgetType,
  118 + title: widgetTypeInfo.name,
  119 + image: widgetTypeInfo.image,
  120 + description: widgetTypeInfo.description
  121 + };
  122 + return widget;
  123 + }
  124 + );
  125 + setTimeout(() => {
  126 + this.widgetTypes = widgetTypes;
  127 + });
  128 + return widgetInfos;
  129 + }),
  130 + tap(() => {
  131 + this.loadingWidgetsSubject.next(false);
  132 + }),
116 publishReplay(1), 133 publishReplay(1),
117 refCount() 134 refCount()
118 ); 135 );
@@ -147,6 +164,7 @@ export class DashboardWidgetSelectComponent implements OnInit { @@ -147,6 +164,7 @@ export class DashboardWidgetSelectComponent implements OnInit {
147 164
148 private getWidgetsBundle(): Observable<Array<WidgetsBundle>> { 165 private getWidgetsBundle(): Observable<Array<WidgetsBundle>> {
149 return this.widgetsService.getAllWidgetsBundles().pipe( 166 return this.widgetsService.getAllWidgetsBundles().pipe(
  167 + tap(() => this.loadingWidgetBundlesSubject.next(false)),
150 publishReplay(1), 168 publishReplay(1),
151 refCount() 169 refCount()
152 ); 170 );
@@ -2266,7 +2266,8 @@ @@ -2266,7 +2266,8 @@
2266 "data-overflow": "Widget displays {{count}} out of {{total}} entities", 2266 "data-overflow": "Widget displays {{count}} out of {{total}} entities",
2267 "alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities", 2267 "alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities",
2268 "search": "Search widget", 2268 "search": "Search widget",
2269 - "filter": "Widget filter type" 2269 + "filter": "Widget filter type",
  2270 + "loading-widgets": "Loading widgets..."
2270 }, 2271 },
2271 "widget-action": { 2272 "widget-action": {
2272 "header-button": "Widget header button", 2273 "header-button": "Widget header button",
@@ -2318,7 +2319,8 @@ @@ -2318,7 +2319,8 @@
2318 "invalid-widgets-bundle-file-error": "Unable to import widgets bundle: Invalid widgets bundle data structure.", 2319 "invalid-widgets-bundle-file-error": "Unable to import widgets bundle: Invalid widgets bundle data structure.",
2319 "search": "Search widget bundles", 2320 "search": "Search widget bundles",
2320 "selected-widgets-bundles": "{ count, plural, 1 {1 widgets bundle} other {# widgets bundles} } selected", 2321 "selected-widgets-bundles": "{ count, plural, 1 {1 widgets bundle} other {# widgets bundles} } selected",
2321 - "open-widgets-bundle": "Open widgets bundle" 2322 + "open-widgets-bundle": "Open widgets bundle",
  2323 + "loading-widgets-bundles": "Loading widgets bundles..."
2322 }, 2324 },
2323 "widget-config": { 2325 "widget-config": {
2324 "data": "Data", 2326 "data": "Data",