thingsboard/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts
2022-05-25 12:26:23 +03:00

245 lines
7.9 KiB
TypeScript

///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityId } from '@shared/models/id/entity-id';
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, merge, Observable, of, ReplaySubject } from 'rxjs';
import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { PageLink } from '@shared/models/page/page-link';
import { catchError, map, tap } from 'rxjs/operators';
import { EntityVersion } from '@shared/models/vc.models';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants';
import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { BranchAutocompleteComponent } from '@shared/components/vc/branch-autocomplete.component';
@Component({
selector: 'tb-entity-versions-table',
templateUrl: './entity-versions-table.component.html',
styleUrls: ['./entity-versions-table.component.scss']
})
export class EntityVersionsTableComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('branchAutocompleteComponent') branchAutocompleteComponent: BranchAutocompleteComponent;
@Input()
singleEntityMode = false;
displayedColumns = ['timestamp', 'id', 'name'];
pageLink: PageLink;
dataSource: EntityVersionsDatasource;
hidePageSize = false;
branch: string = null;
activeValue = false;
dirtyValue = false;
externalEntityIdValue: EntityId;
viewsInited = false;
private componentResize$: ResizeObserver;
@Input()
set active(active: boolean) {
if (this.activeValue !== active) {
this.activeValue = active;
if (this.activeValue && this.dirtyValue) {
this.dirtyValue = false;
if (this.viewsInited) {
this.initFromDefaultBranch();
}
}
}
}
@Input()
set externalEntityId(externalEntityId: EntityId) {
if (this.externalEntityIdValue !== externalEntityId) {
this.externalEntityIdValue = externalEntityId;
this.resetSortAndFilter(this.activeValue);
if (!this.activeValue) {
this.dirtyValue = true;
}
}
}
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService,
private cd: ChangeDetectorRef,
private elementRef: ElementRef) {
super(store);
this.dirtyValue = !this.activeValue;
const sortOrder: SortOrder = { property: 'timestamp', direction: Direction.DESC };
this.pageLink = new PageLink(10, 0, null, sortOrder);
this.dataSource = new EntityVersionsDatasource(this.entitiesVersionControlService);
}
ngOnInit() {
this.componentResize$ = new ResizeObserver(() => {
const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue;
if (showHidePageSize !== this.hidePageSize) {
this.hidePageSize = showHidePageSize;
this.cd.markForCheck();
}
});
this.componentResize$.observe(this.elementRef.nativeElement);
}
ngOnDestroy() {
if (this.componentResize$) {
this.componentResize$.disconnect();
}
}
branchChanged(newBranch: string) {
this.branch = newBranch;
this.paginator.pageIndex = 0;
if (this.activeValue) {
this.updateData();
}
}
ngAfterViewInit() {
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
merge(this.sort.sortChange, this.paginator.page)
.pipe(
tap(() => this.updateData())
)
.subscribe();
this.viewsInited = true;
if (!this.singleEntityMode) {
this.initFromDefaultBranch();
}
}
vcExport($event: Event) {
if ($event) {
$event.stopPropagation();
}
}
private initFromDefaultBranch() {
this.branchAutocompleteComponent.selectDefaultBranchIfNeeded(false, true);
}
private updateData() {
this.pageLink.page = this.paginator.pageIndex;
this.pageLink.pageSize = this.paginator.pageSize;
this.pageLink.sortOrder.property = this.sort.active;
this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()];
this.dataSource.loadEntityVersions(this.singleEntityMode, this.branch, this.externalEntityIdValue, this.pageLink);
}
private resetSortAndFilter(update: boolean) {
this.branch = null;
this.pageLink.textSearch = null;
if (this.viewsInited) {
this.paginator.pageIndex = 0;
const sortable = this.sort.sortables.get('timestamp');
this.sort.active = sortable.id;
this.sort.direction = 'desc';
if (update) {
this.initFromDefaultBranch();
}
}
}
}
class EntityVersionsDatasource implements DataSource<EntityVersion> {
private entityVersionsSubject = new BehaviorSubject<EntityVersion[]>([]);
private pageDataSubject = new BehaviorSubject<PageData<EntityVersion>>(emptyPageData<EntityVersion>());
public pageData$ = this.pageDataSubject.asObservable();
constructor(private entitiesVersionControlService: EntitiesVersionControlService) {}
connect(collectionViewer: CollectionViewer): Observable<EntityVersion[] | ReadonlyArray<EntityVersion>> {
return this.entityVersionsSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.entityVersionsSubject.complete();
this.pageDataSubject.complete();
}
loadEntityVersions(singleEntityMode: boolean,
branch: string, externalEntityId: EntityId,
pageLink: PageLink): Observable<PageData<EntityVersion>> {
const result = new ReplaySubject<PageData<EntityVersion>>();
this.fetchEntityVersions(singleEntityMode, branch, externalEntityId, pageLink).pipe(
catchError(() => of(emptyPageData<EntityVersion>())),
).subscribe(
(pageData) => {
this.entityVersionsSubject.next(pageData.data);
this.pageDataSubject.next(pageData);
result.next(pageData);
}
);
return result;
}
fetchEntityVersions(singleEntityMode: boolean,
branch: string, externalEntityId: EntityId,
pageLink: PageLink): Observable<PageData<EntityVersion>> {
if (!branch) {
return of(emptyPageData<EntityVersion>());
} else {
if (singleEntityMode) {
if (externalEntityId) {
return this.entitiesVersionControlService.listEntityVersions(pageLink, branch, externalEntityId, {ignoreErrors: true});
} else {
return of(emptyPageData<EntityVersion>());
}
} else {
return this.entitiesVersionControlService.listVersions(pageLink, branch, {ignoreErrors: true});
}
}
}
isEmpty(): Observable<boolean> {
return this.entityVersionsSubject.pipe(
map((entityVersions) => !entityVersions.length)
);
}
total(): Observable<number> {
return this.pageDataSubject.pipe(
map((pageData) => pageData.totalElements)
);
}
}