From 33ad8ef8c5e29e63fdbc0f6883689b455088d478 Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Fri, 7 Oct 2022 16:24:22 -0700 Subject: [PATCH] [EC-86] Update initialization/loading logic to make better use of the Observable pattern --- .../organizations/manage/groups.component.ts | 98 ++++++++++++------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/apps/web/src/app/organizations/manage/groups.component.ts b/apps/web/src/app/organizations/manage/groups.component.ts index f9052a6a489..44d44732bfc 100644 --- a/apps/web/src/app/organizations/manage/groups.component.ts +++ b/apps/web/src/app/organizations/manage/groups.component.ts @@ -1,6 +1,16 @@ import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { concatMap, Subject, takeUntil } from "rxjs"; +import { + BehaviorSubject, + combineLatest, + concatMap, + from, + map, + Subject, + switchMap, + takeUntil, + tap, +} from "rxjs"; import { first } from "rxjs/operators"; import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; @@ -15,8 +25,12 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { CollectionData } from "@bitwarden/common/models/data/collectionData"; import { Collection } from "@bitwarden/common/models/domain/collection"; import { OrganizationGroupBulkRequest } from "@bitwarden/common/models/request/OrganizationGroupBulkRequest"; -import { CollectionDetailsResponse } from "@bitwarden/common/models/response/collectionResponse"; +import { + CollectionDetailsResponse, + CollectionResponse, +} from "@bitwarden/common/models/response/collectionResponse"; import { IGroupDetailsResponse } from "@bitwarden/common/models/response/groupResponse"; +import { ListResponse } from "@bitwarden/common/models/response/listResponse"; import { CollectionView } from "@bitwarden/common/models/view/collectionView"; import { EntityUsersComponent } from "./entity-users.component"; @@ -61,6 +75,7 @@ export class GroupsComponent implements OnInit, OnDestroy { private searchedGroups: IGroupDetailsRow[]; private _searchText: string; private destroy$ = new Subject(); + private refreshGroups$ = new BehaviorSubject(null); get searchText() { return this._searchText; @@ -100,16 +115,43 @@ export class GroupsComponent implements OnInit, OnDestroy { ) {} async ngOnInit() { - this.route.parent.params + this.route.params .pipe( - concatMap(async (params) => { - this.organizationId = params.organizationId; - await this.loadCollections(); - await this.load(); + tap((params) => (this.organizationId = params.organizationId)), + switchMap(() => + combineLatest([ + // collectionMap + from(this.apiService.getCollections(this.organizationId)).pipe( + concatMap((response) => this.toCollectionMap(response)) + ), + // groups + this.refreshGroups$.pipe( + switchMap(() => this.apiService.getGroups(this.organizationId)), + map((response) => + response.data != null && response.data.length > 0 ? response.data : [] + ) + ), + ]) + ), + map(([collectionMap, groups]) => { + return groups + .sort(Utils.getSortFunction(this.i18nService, "name")) + .map((g) => ({ + ...g, + checked: false, + collectionNames: g.collections + .map((c) => collectionMap[c.id]?.name) + .sort(this.i18nService.collator?.compare), + })); }), takeUntil(this.destroy$) ) - .subscribe(); + .subscribe((groups) => { + this.groups = groups; + this.resetPaging(); + this.updateSearchedGroups(); + this.loading = false; + }); this.route.queryParams .pipe( @@ -127,21 +169,17 @@ export class GroupsComponent implements OnInit, OnDestroy { this.destroy$.complete(); } - async load() { - const response = await this.apiService.getGroups(this.organizationId); - const groups = response.data != null && response.data.length > 0 ? response.data : []; - this.groups = groups - .sort(Utils.getSortFunction(this.i18nService, "name")) - .map((g) => ({ - ...g, - checked: false, - collectionNames: g.collections - .map((c) => this.collectionMap[c.id]?.name) - .sort(this.i18nService.collator?.compare), - })); - this.resetPaging(); - this.updateSearchedGroups(); - this.loading = false; + async toCollectionMap(response: ListResponse) { + const collections = response.data.map( + (r) => new Collection(new CollectionData(r as CollectionDetailsResponse)) + ); + const decryptedCollections = await this.collectionService.decryptMany(collections); + + // Convert to an object using collection Ids as keys for faster name lookups + const collectionMap: CollectionViewMap = {}; + decryptedCollections.forEach((c) => (collectionMap[c.id] = c)); + + return collectionMap; } private updateSearchedGroups() { @@ -151,18 +189,6 @@ export class GroupsComponent implements OnInit, OnDestroy { } } - async loadCollections() { - const response = await this.apiService.getCollections(this.organizationId); - const collections = response.data.map( - (r) => new Collection(new CollectionData(r as CollectionDetailsResponse)) - ); - const decryptedCollections = await this.collectionService.decryptMany(collections); - - // Convert to an object using collection Ids as keys for faster name lookups - this.collectionMap = {}; - decryptedCollections.forEach((c) => (this.collectionMap[c.id] = c)); - } - loadMore() { if (!this.groups || this.groups.length <= this.pageSize) { return; @@ -190,7 +216,7 @@ export class GroupsComponent implements OnInit, OnDestroy { comp.groupId = group != null ? group.id : null; comp.onSavedGroup.pipe(takeUntil(this.destroy$)).subscribe(() => { modal.close(); - this.load(); + this.refreshGroups$.next(); }); comp.onDeletedGroup.pipe(takeUntil(this.destroy$)).subscribe(() => { modal.close();