Commit 10ddb5f8f37581611df15cb154127ce1f4d683bd
1 parent
67dd34db
Fix timeseries widget (invoke data updated callback from data aggregator on init…
…ial data). Improve widget selector.
Showing
8 changed files
with
123 additions
and
64 deletions
@@ -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", |