[AC-779] [Defect] Event log links for policies groups users and items not working (#5212)

* [AC-779] fix: policy link

* [AC-779] fix: search string set by url not showing in input field

* [AC-779] fix: navigation to cipher events

* [AC-779] fix: collection link

* [AC-779] chore: clean up old components

* [AC-779] chore: remove some copy pasta
This commit is contained in:
Andreas Coroiu 2023-04-18 08:04:39 +02:00 committed by GitHub
parent d77f77cea9
commit 4852992662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 67 additions and 512 deletions

View File

@ -1,106 +0,0 @@
<div class="page-header d-flex">
<h1>{{ "collections" | i18n }}</h1>
<div class="ml-auto d-flex">
<div>
<label class="sr-only" for="search">{{ "search" | i18n }}</label>
<input
type="search"
class="form-control form-control-sm"
id="search"
placeholder="{{ 'search' | i18n }}"
[(ngModel)]="searchText"
/>
</div>
<button
type="button"
*ngIf="this.canCreate"
class="btn btn-sm btn-outline-primary ml-3"
(click)="add()"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "newCollection" | i18n }}
</button>
</div>
</div>
<ng-container *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container
*ngIf="
!loading &&
(isPaging()
? pagedCollections
: (collections | search : searchText : 'name' : 'id')) as searchedCollections
"
>
<p *ngIf="!searchedCollections.length">{{ "noCollectionsInList" | i18n }}</p>
<table
class="table table-hover table-list"
*ngIf="searchedCollections.length"
infiniteScroll
[infiniteScrollDistance]="1"
[infiniteScrollDisabled]="!isPaging()"
(scrolled)="loadMore()"
>
<tbody>
<tr *ngFor="let c of searchedCollections">
<td>
<a href="#" appStopClick (click)="edit(c)">{{ c.name }}</a>
</td>
<td class="table-list-options">
<div class="dropdown" appListDropdown *ngIf="this.canEdit(c) || this.canDelete(c)">
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
appA11yTitle="{{ 'options' | i18n }}"
>
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
</button>
<div class="dropdown-menu dropdown-menu-right">
<a
class="dropdown-item"
href="#"
appStopClick
*ngIf="this.canEdit(c)"
(click)="edit(c)"
>
<i class="bwi bwi-fw bwi-pencil-square" aria-hidden="true"></i>
{{ "edit" | i18n }}
</a>
<a
class="dropdown-item"
href="#"
appStopClick
*ngIf="this.canEdit(c)"
(click)="users(c)"
>
<i class="bwi bwi-fw bwi-users" aria-hidden="true"></i>
{{ "users" | i18n }}
</a>
<a
class="dropdown-item text-danger"
href="#"
appStopClick
*ngIf="this.canDelete(c)"
(click)="delete(c)"
>
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
{{ "delete" | i18n }}
</a>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</ng-container>
<ng-template #addEdit></ng-template>
<ng-template #usersTemplate></ng-template>

View File

@ -1,298 +0,0 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { firstValueFrom, lastValueFrom } from "rxjs";
import { first } from "rxjs/operators";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { CollectionService } from "@bitwarden/common/admin-console/abstractions/collection.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { CollectionData } from "@bitwarden/common/admin-console/models/data/collection.data";
import { Collection } from "@bitwarden/common/admin-console/models/domain/collection";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import {
CollectionDetailsResponse,
CollectionResponse,
} from "@bitwarden/common/admin-console/models/response/collection.response";
import { CollectionView } from "@bitwarden/common/admin-console/models/view/collection.view";
import { ProductType } from "@bitwarden/common/enums";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import {
DialogService,
SimpleDialogCloseType,
SimpleDialogOptions,
SimpleDialogType,
} from "@bitwarden/components";
import { EntityUsersComponent } from "../manage/entity-users.component";
import {
CollectionDialogResult,
openCollectionDialog,
} from "../shared/components/collection-dialog";
@Component({
selector: "app-org-manage-collections",
templateUrl: "collections.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class CollectionsComponent implements OnInit {
@ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
@ViewChild("usersTemplate", { read: ViewContainerRef, static: true })
usersModalRef: ViewContainerRef;
loading = true;
organization: Organization;
canCreate = false;
organizationId: string;
collections: CollectionView[];
assignedCollections: CollectionView[];
pagedCollections: CollectionView[];
searchText: string;
protected didScroll = false;
protected pageSize = 100;
private pagedCollectionsCount = 0;
constructor(
private apiService: ApiService,
private route: ActivatedRoute,
private collectionService: CollectionService,
private modalService: ModalService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private searchService: SearchService,
private logService: LogService,
private organizationService: OrganizationService,
private dialogService: DialogService,
private router: Router
) {}
async ngOnInit() {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
await this.load();
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
this.searchText = qParams.search;
});
});
}
async load() {
this.organization = await this.organizationService.get(this.organizationId);
this.canCreate = this.organization.canCreateNewCollections;
const decryptCollections = async (r: ListResponse<CollectionResponse>) => {
const collections = r.data
.filter((c) => c.organizationId === this.organizationId)
.map((d) => new Collection(new CollectionData(d as CollectionDetailsResponse)));
return await this.collectionService.decryptMany(collections);
};
if (this.organization.canViewAssignedCollections) {
const response = await this.apiService.getUserCollections();
this.assignedCollections = await decryptCollections(response);
}
if (this.organization.canViewAllCollections) {
const response = await this.apiService.getCollections(this.organizationId);
this.collections = await decryptCollections(response);
} else {
this.collections = this.assignedCollections;
}
this.resetPaging();
this.loading = false;
}
loadMore() {
if (!this.collections || this.collections.length <= this.pageSize) {
return;
}
const pagedLength = this.pagedCollections.length;
let pagedSize = this.pageSize;
if (pagedLength === 0 && this.pagedCollectionsCount > this.pageSize) {
pagedSize = this.pagedCollectionsCount;
}
if (this.collections.length > pagedLength) {
this.pagedCollections = this.pagedCollections.concat(
this.collections.slice(pagedLength, pagedLength + pagedSize)
);
}
this.pagedCollectionsCount = this.pagedCollections.length;
this.didScroll = this.pagedCollections.length > this.pageSize;
}
async edit(collection?: CollectionView) {
const canCreate = collection == undefined && this.canCreate;
const canEdit = collection != undefined && this.canEdit(collection);
const canDelete = collection != undefined && this.canDelete(collection);
if (!(canCreate || canEdit || canDelete)) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("missingPermissions"));
return;
}
if (
!collection &&
this.organization.planProductType === ProductType.Free &&
this.collections.length === this.organization.maxCollections
) {
// Show org upgrade modal
// It might be worth creating a simple
// org upgrade dialog service to launch the dialog here and in the people.comp
// once the enterprise pod is done w/ their organization module refactor.
const orgUpgradeSimpleDialogOpts: SimpleDialogOptions = {
title: this.i18nService.t("upgradeOrganization"),
content: this.i18nService.t(
this.organization.canEditSubscription
? "freeOrgMaxCollectionReachedManageBilling"
: "freeOrgMaxCollectionReachedNoManageBilling",
this.organization.maxCollections
),
type: SimpleDialogType.PRIMARY,
};
if (this.organization.canEditSubscription) {
orgUpgradeSimpleDialogOpts.acceptButtonText = this.i18nService.t("upgrade");
} else {
orgUpgradeSimpleDialogOpts.acceptButtonText = this.i18nService.t("ok");
orgUpgradeSimpleDialogOpts.cancelButtonText = null; // hide secondary btn
}
const simpleDialog = this.dialogService.openSimpleDialog(orgUpgradeSimpleDialogOpts);
firstValueFrom(simpleDialog.closed).then((result: SimpleDialogCloseType | undefined) => {
if (!result) {
return;
}
if (result == SimpleDialogCloseType.ACCEPT && this.organization.canEditSubscription) {
this.router.navigate(
["/organizations", this.organization.id, "billing", "subscription"],
{ queryParams: { upgrade: true } }
);
}
});
return;
}
const dialog = openCollectionDialog(this.dialogService, {
data: { collectionId: collection?.id, organizationId: this.organizationId },
});
const result = await lastValueFrom(dialog.closed);
if (result === CollectionDialogResult.Saved || result === CollectionDialogResult.Deleted) {
this.load();
}
}
add() {
this.edit(null);
}
async delete(collection: CollectionView) {
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("deleteCollectionConfirmation"),
collection.name,
this.i18nService.t("yes"),
this.i18nService.t("no"),
"warning"
);
if (!confirmed) {
return false;
}
try {
await this.apiService.deleteCollection(this.organizationId, collection.id);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("deletedCollectionId", collection.name)
);
this.removeCollection(collection);
} catch (e) {
this.logService.error(e);
this.platformUtilsService.showToast("error", null, this.i18nService.t("missingPermissions"));
}
}
async users(collection: CollectionView) {
const [modal] = await this.modalService.openViewRef(
EntityUsersComponent,
this.usersModalRef,
(comp) => {
comp.organizationId = this.organizationId;
comp.entity = "collection";
comp.entityId = collection.id;
comp.entityName = collection.name;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
comp.onEditedUsers.subscribe(() => {
this.load();
modal.close();
});
}
);
}
async resetPaging() {
this.pagedCollections = [];
this.loadMore();
}
isSearching() {
return this.searchService.isSearchable(this.searchText);
}
isPaging() {
const searching = this.isSearching();
if (searching && this.didScroll) {
this.resetPaging();
}
return !searching && this.collections && this.collections.length > this.pageSize;
}
canEdit(collection: CollectionView) {
if (this.organization.canEditAnyCollection) {
return true;
}
if (
this.organization.canEditAssignedCollections &&
this.assignedCollections.some((c) => c.id === collection.id)
) {
return true;
}
return false;
}
canDelete(collection: CollectionView) {
if (this.organization.canDeleteAnyCollection) {
return true;
}
if (
this.organization.canDeleteAssignedCollections &&
this.assignedCollections.some((c) => c.id === collection.id)
) {
return true;
}
return false;
}
private removeCollection(collection: CollectionView) {
const index = this.collections.indexOf(collection);
if (index > -1) {
this.collections.splice(index, 1);
this.resetPaging();
}
}
}

View File

@ -1,38 +0,0 @@
<div class="container page-content">
<div class="row">
<div class="col-3">
<div class="card" *ngIf="organization">
<div class="card-header">{{ "manage" | i18n }}</div>
<div class="list-group list-group-flush">
<a
routerLink="members"
class="list-group-item"
routerLinkActive="active"
*ngIf="organization.canManageUsers"
>
{{ "members" | i18n }}
</a>
<a
routerLink="collections"
class="list-group-item"
routerLinkActive="active"
*ngIf="organization.canViewAllCollections || organization.canViewAssignedCollections"
>
{{ "collections" | i18n }}
</a>
<a
routerLink="groups"
class="list-group-item"
routerLinkActive="active"
*ngIf="organization.canManageGroups"
>
{{ "groups" | i18n }}
</a>
</div>
</div>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
</div>
</div>

View File

@ -1,23 +0,0 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
@Component({
selector: "app-org-manage",
templateUrl: "manage.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class ManageComponent implements OnInit {
organization: Organization;
constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {}
ngOnInit() {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.parent.params.subscribe(async (params) => {
this.organization = await this.organizationService.get(params.organizationId);
});
}
}

View File

@ -15,9 +15,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { OrganizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
import { OrganizationRedirectGuard } from "../../admin-console/organizations/guards/org-redirect.guard";
import { OrganizationLayoutComponent } from "../../admin-console/organizations/layouts/organization-layout.component";
import { CollectionsComponent } from "../../admin-console/organizations/manage/collections.component";
import { GroupsComponent } from "../../admin-console/organizations/manage/groups.component";
import { ManageComponent } from "../../admin-console/organizations/manage/manage.component";
import { VaultModule } from "../../vault/org-vault/vault.module";
const routes: Routes = [
@ -62,19 +60,6 @@ const routes: Routes = [
organizationPermissions: canAccessGroupsTab,
},
},
{
path: "manage",
component: ManageComponent,
children: [
{
path: "collections",
component: CollectionsComponent,
data: {
titleId: "collections",
},
},
],
},
{
path: "reporting",
loadChildren: () =>

View File

@ -487,12 +487,7 @@ export class EventService {
const a = this.makeAnchor(shortId);
a.setAttribute(
"href",
"#/organizations/" +
ev.organizationId +
"/vault?search=" +
shortId +
"&viewEvents=" +
ev.cipherId
`#/organizations/${ev.organizationId}/vault?search=${shortId}&viewEvents=${ev.cipherId}&type=all`
);
return a.outerHTML;
}
@ -507,10 +502,9 @@ export class EventService {
private formatCollectionId(ev: EventResponse) {
const shortId = this.getShortId(ev.collectionId);
const a = this.makeAnchor(shortId);
// TODO: Update view/edit collection link after EC-14 is completed
a.setAttribute(
"href",
"#/organizations/" + ev.organizationId + "/manage/collections?search=" + shortId
`#/organizations/${ev.organizationId}/vault?collectionId=${ev.collectionId}`
);
return a.outerHTML;
}
@ -557,7 +551,7 @@ export class EventService {
const a = this.makeAnchor(shortId);
a.setAttribute(
"href",
"#/organizations/" + ev.organizationId + "/manage/policies?policyId=" + ev.policyId
"#/organizations/" + ev.organizationId + "/settings/policies?policyId=" + ev.policyId
);
return a.outerHTML;
}

View File

@ -36,10 +36,8 @@ import { VerifyRecoverDeleteComponent } from "../../auth/verify-recover-delete.c
import { OrganizationSwitcherComponent } from "../admin-console/components/organization-switcher.component";
import { OrganizationCreateModule } from "../admin-console/organizations/create/organization-create.module";
import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.component";
import { CollectionsComponent as OrgManageCollectionsComponent } from "../admin-console/organizations/manage/collections.component";
import { EntityEventsComponent as OrgEntityEventsComponent } from "../admin-console/organizations/manage/entity-events.component";
import { EventsComponent as OrgEventsComponent } from "../admin-console/organizations/manage/events.component";
import { ManageComponent as OrgManageComponent } from "../admin-console/organizations/manage/manage.component";
import { UserConfirmComponent as OrgUserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component";
import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component";
import { FamiliesForEnterpriseSetupComponent } from "../admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
@ -172,8 +170,6 @@ import { SharedModule } from "./shared.module";
OrgEventsComponent,
OrgExposedPasswordsReportComponent,
OrgInactiveTwoFactorReportComponent,
OrgManageCollectionsComponent,
OrgManageComponent,
OrgReusedPasswordsReportComponent,
OrgToolsComponent,
OrgUnsecuredWebsitesReportComponent,
@ -281,8 +277,6 @@ import { SharedModule } from "./shared.module";
OrgEventsComponent,
OrgExposedPasswordsReportComponent,
OrgInactiveTwoFactorReportComponent,
OrgManageCollectionsComponent,
OrgManageComponent,
OrgReusedPasswordsReportComponent,
OrgToolsComponent,
OrgUnsecuredWebsitesReportComponent,

View File

@ -21,7 +21,8 @@
placeholder="{{ searchPlaceholder | i18n }}"
id="search"
class="form-control"
(input)="searchTextChanged($any($event.target).value)"
[ngModel]="searchText"
(ngModelChange)="onSearchTextChanged($event)"
autocomplete="off"
appAutofocus
/>

View File

@ -32,12 +32,13 @@ import { OrganizationOptionsComponent } from "./organization-options.component";
export class VaultFilterComponent implements OnInit, OnDestroy {
filters?: VaultFilterList;
@Input() activeFilter: VaultFilter = new VaultFilter();
@Output() onSearchTextChanged = new EventEmitter<string>();
@Output() onAddFolder = new EventEmitter<never>();
@Output() onEditFolder = new EventEmitter<FolderFilter>();
@Input() searchText = "";
@Output() searchTextChanged = new EventEmitter<string>();
isLoaded = false;
searchText = "";
protected destroy$: Subject<void> = new Subject<void>();
@ -99,9 +100,9 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
this.destroy$.complete();
}
searchTextChanged(t: string) {
onSearchTextChanged(t: string) {
this.searchText = t;
this.onSearchTextChanged.emit(t);
this.searchTextChanged.emit(t);
}
applyOrganizationFilter = async (orgNode: TreeNode<OrganizationFilter>): Promise<void> => {

View File

@ -7,7 +7,8 @@
<app-vault-filter
#vaultFilter
[activeFilter]="activeFilter"
(onSearchTextChanged)="filterSearchText($event)"
[searchText]="currentSearchText$ | async"
(searchTextChanged)="filterSearchText($event)"
(onAddFolder)="addFolder()"
(onEditFolder)="editFolder($event)"
></app-vault-filter>

View File

@ -8,7 +8,14 @@ import {
ViewContainerRef,
} from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { BehaviorSubject, combineLatest, firstValueFrom, lastValueFrom, Subject } from "rxjs";
import {
BehaviorSubject,
combineLatest,
firstValueFrom,
lastValueFrom,
Observable,
Subject,
} from "rxjs";
import {
concatMap,
debounceTime,
@ -131,9 +138,10 @@ export class VaultComponent implements OnInit, OnDestroy {
protected collections: CollectionView[];
protected isEmpty: boolean;
protected selectedCollection: TreeNode<CollectionView> | undefined;
protected currentSearchText$: Observable<string>;
private refresh$ = new BehaviorSubject<void>(null);
private searchText$ = new Subject<string>();
private refresh$ = new BehaviorSubject<void>(null);
private destroy$ = new Subject<void>();
constructor(
@ -242,12 +250,12 @@ export class VaultComponent implements OnInit, OnDestroy {
})
);
const querySearchText$ = this.route.queryParams.pipe(map((queryParams) => queryParams.search));
this.currentSearchText$ = this.route.queryParams.pipe(map((queryParams) => queryParams.search));
const ciphers$ = combineLatest([
Utils.asyncToObservable(() => this.cipherService.getAllDecrypted()),
filter$,
querySearchText$,
this.currentSearchText$,
]).pipe(
filter(([ciphers, filter]) => ciphers != undefined && filter != undefined),
concatMap(async ([ciphers, filter, searchText]) => {
@ -262,7 +270,7 @@ export class VaultComponent implements OnInit, OnDestroy {
shareReplay({ refCount: true, bufferSize: 1 })
);
const collections$ = combineLatest([nestedCollections$, filter$, querySearchText$]).pipe(
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
filter(([collections, filter]) => collections != undefined && filter != undefined),
map(([collections, filter, searchText]) => {
if (filter.collectionId === undefined || filter.collectionId === Unassigned) {

View File

@ -10,7 +10,8 @@
#vaultFilter
[organization]="organization"
[activeFilter]="activeFilter"
(onSearchTextChanged)="filterSearchText($event)"
[searchText]="currentSearchText$ | async"
(searchTextChanged)="filterSearchText($event)"
></app-organization-vault-filter>
</div>
</div>

View File

@ -8,7 +8,14 @@ import {
ViewContainerRef,
} from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { BehaviorSubject, combineLatest, firstValueFrom, lastValueFrom, Subject } from "rxjs";
import {
BehaviorSubject,
combineLatest,
firstValueFrom,
lastValueFrom,
Observable,
Subject,
} from "rxjs";
import {
concatMap,
debounceTime,
@ -123,9 +130,10 @@ export class VaultComponent implements OnInit, OnDestroy {
protected selectedCollection: TreeNode<CollectionAdminView> | undefined;
protected isEmpty: boolean;
protected showMissingCollectionPermissionMessage: boolean;
protected currentSearchText$: Observable<string>;
private refresh$ = new BehaviorSubject<void>(null);
private searchText$ = new Subject<string>();
private refresh$ = new BehaviorSubject<void>(null);
private destroy$ = new Subject<void>();
constructor(
@ -219,7 +227,7 @@ export class VaultComponent implements OnInit, OnDestroy {
})
);
const querySearchText$ = this.route.queryParams.pipe(map((queryParams) => queryParams.search));
this.currentSearchText$ = this.route.queryParams.pipe(map((queryParams) => queryParams.search));
const allCollectionsWithoutUnassigned$ = organizationId$.pipe(
switchMap((orgId) => this.collectionAdminService.getAll(orgId)),
@ -256,7 +264,7 @@ export class VaultComponent implements OnInit, OnDestroy {
})
);
const ciphers$ = combineLatest([allCiphers$, filter$, querySearchText$]).pipe(
const ciphers$ = combineLatest([allCiphers$, filter$, this.currentSearchText$]).pipe(
filter(([ciphers, filter]) => ciphers != undefined && filter != undefined),
concatMap(async ([ciphers, filter, searchText]) => {
if (filter.collectionId === undefined && filter.type === undefined) {
@ -279,7 +287,7 @@ export class VaultComponent implements OnInit, OnDestroy {
shareReplay({ refCount: true, bufferSize: 1 })
);
const collections$ = combineLatest([nestedCollections$, filter$, querySearchText$]).pipe(
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
filter(([collections, filter]) => collections != undefined && filter != undefined),
map(([collections, filter, searchText]) => {
if (
@ -379,6 +387,33 @@ export class VaultComponent implements OnInit, OnDestroy {
)
.subscribe();
firstSetup$
.pipe(
switchMap(() => combineLatest([this.route.queryParams, organization$, allCiphers$])),
switchMap(async ([qParams, organization, allCiphers$]) => {
const cipherId = qParams.viewEvents;
if (!cipherId) {
return;
}
const cipher = allCiphers$.find((c) => c.id === cipherId);
if (organization.useEvents && cipher != undefined) {
this.viewEvents(cipher);
} else {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("unknownCipher")
);
this.router.navigate([], {
queryParams: { viewEvents: null },
queryParamsHandling: "merge",
});
}
}),
takeUntil(this.destroy$)
)
.subscribe();
firstSetup$
.pipe(
switchMap(() => this.refresh$),