import { ChangeDetectorRef, Component, ComponentFactoryResolver, NgZone, OnDestroy, OnInit, ViewChild, ViewContainerRef, } from '@angular/core'; import { ActivatedRoute, Router, } from '@angular/router'; import { I18nService } from 'jslib/abstractions/i18n.service'; import { MessagingService } from 'jslib/abstractions/messaging.service'; import { SyncService } from 'jslib/abstractions/sync.service'; import { UserService } from 'jslib/abstractions/user.service'; import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; import { Organization } from 'jslib/models/domain/organization'; import { CipherView } from 'jslib/models/view/cipherView'; import { CipherType } from 'jslib/enums/cipherType'; import { ModalComponent } from '../../modal.component'; import { EntityEventsComponent } from '../manage/entity-events.component'; import { AddEditComponent } from './add-edit.component'; import { AttachmentsComponent } from './attachments.component'; import { CiphersComponent } from './ciphers.component'; import { CollectionsComponent } from './collections.component'; import { GroupingsComponent } from './groupings.component'; const BroadcasterSubscriptionId = 'OrgVaultComponent'; @Component({ selector: 'app-org-vault', templateUrl: 'vault.component.html', }) export class VaultComponent implements OnInit, OnDestroy { @ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent; @ViewChild(CiphersComponent) ciphersComponent: CiphersComponent; @ViewChild('attachments', { read: ViewContainerRef }) attachmentsModalRef: ViewContainerRef; @ViewChild('cipherAddEdit', { read: ViewContainerRef }) cipherAddEditModalRef: ViewContainerRef; @ViewChild('collections', { read: ViewContainerRef }) collectionsModalRef: ViewContainerRef; @ViewChild('eventsTemplate', { read: ViewContainerRef }) eventsModalRef: ViewContainerRef; organization: Organization; collectionId: string; type: CipherType; private modal: ModalComponent = null; constructor(private route: ActivatedRoute, private userService: UserService, private router: Router, private changeDetectorRef: ChangeDetectorRef, private syncService: SyncService, private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService, private broadcasterService: BroadcasterService, private ngZone: NgZone) { } ngOnInit() { this.route.parent.params.subscribe(async (params) => { this.organization = await this.userService.getOrganization(params.organizationId); this.groupingsComponent.organization = this.organization; this.ciphersComponent.organization = this.organization; const queryParamsSub = this.route.queryParams.subscribe(async (qParams) => { this.ciphersComponent.searchText = this.groupingsComponent.searchText = qParams.search; if (!this.organization.isAdmin) { await this.syncService.fullSync(false); this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { this.ngZone.run(async () => { switch (message.command) { case 'syncCompleted': if (message.successfully) { await Promise.all([ this.groupingsComponent.load(), this.ciphersComponent.refresh(), ]); this.changeDetectorRef.detectChanges(); } break; } }); }); } await this.groupingsComponent.load(); if (qParams == null) { this.groupingsComponent.selectedAll = true; await this.ciphersComponent.load(); } else { if (qParams.type) { const t = parseInt(qParams.type, null); this.groupingsComponent.selectedType = t; await this.filterCipherType(t, true); } else if (qParams.collectionId) { this.groupingsComponent.selectedCollectionId = qParams.collectionId; await this.filterCollection(qParams.collectionId, true); } else { this.groupingsComponent.selectedAll = true; await this.ciphersComponent.load(); } } if (qParams.viewEvents != null) { const cipher = this.ciphersComponent.ciphers.filter((c) => c.id === qParams.viewEvents); if (cipher.length > 0) { this.viewEvents(cipher[0]); } } if (queryParamsSub != null) { queryParamsSub.unsubscribe(); } }); }); } ngOnDestroy() { this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); } async clearGroupingFilters() { this.ciphersComponent.showAddNew = true; this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchVault'); await this.ciphersComponent.applyFilter(); this.clearFilters(); this.go(); } async filterCipherType(type: CipherType, load = false) { this.ciphersComponent.showAddNew = true; this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchType'); const filter = (c: CipherView) => c.type === type; if (load) { await this.ciphersComponent.load(filter); } else { await this.ciphersComponent.applyFilter(filter); } this.clearFilters(); this.type = type; this.go(); } async filterCollection(collectionId: string, load = false) { this.ciphersComponent.showAddNew = true; this.groupingsComponent.searchPlaceholder = this.i18nService.t('searchCollection'); const filter = (c: CipherView) => { if (collectionId === 'unassigned') { return c.collectionIds == null || c.collectionIds.length === 0; } else { return c.collectionIds != null && c.collectionIds.indexOf(collectionId) > -1; } }; if (load) { await this.ciphersComponent.load(filter); } else { await this.ciphersComponent.applyFilter(filter); } this.clearFilters(); this.collectionId = collectionId; this.go(); } filterSearchText(searchText: string) { this.ciphersComponent.searchText = searchText; this.ciphersComponent.search(200); } editCipherAttachments(cipher: CipherView) { if (this.organization.maxStorageGb == null || this.organization.maxStorageGb === 0) { this.messagingService.send('upgradeOrganization', { organizationId: cipher.organizationId }); return; } if (this.modal != null) { this.modal.close(); } const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); this.modal = this.attachmentsModalRef.createComponent(factory).instance; const childComponent = this.modal.show(AttachmentsComponent, this.attachmentsModalRef); childComponent.organization = this.organization; childComponent.cipherId = cipher.id; let madeAttachmentChanges = false; childComponent.onUploadedAttachment.subscribe(() => madeAttachmentChanges = true); childComponent.onDeletedAttachment.subscribe(() => madeAttachmentChanges = true); this.modal.onClosed.subscribe(async () => { this.modal = null; if (madeAttachmentChanges) { await this.ciphersComponent.refresh(); } madeAttachmentChanges = false; }); } editCipherCollections(cipher: CipherView) { if (this.modal != null) { this.modal.close(); } const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); this.modal = this.collectionsModalRef.createComponent(factory).instance; const childComponent = this.modal.show(CollectionsComponent, this.collectionsModalRef); if (this.organization.isAdmin) { childComponent.collectionIds = cipher.collectionIds; childComponent.collections = this.groupingsComponent.collections.filter((c) => !c.readOnly); } childComponent.organization = this.organization; childComponent.cipherId = cipher.id; childComponent.onSavedCollections.subscribe(async () => { this.modal.close(); await this.ciphersComponent.refresh(); }); this.modal.onClosed.subscribe(async () => { this.modal = null; }); } addCipher() { const component = this.editCipher(null); component.organizationId = this.organization.id; component.type = this.type; if (this.organization.isAdmin) { component.collections = this.groupingsComponent.collections.filter((c) => !c.readOnly); } if (this.collectionId != null) { component.collectionIds = [this.collectionId]; } } editCipher(cipher: CipherView) { if (this.modal != null) { this.modal.close(); } const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); this.modal = this.cipherAddEditModalRef.createComponent(factory).instance; const childComponent = this.modal.show(AddEditComponent, this.cipherAddEditModalRef); childComponent.organization = this.organization; childComponent.cipherId = cipher == null ? null : cipher.id; childComponent.onSavedCipher.subscribe(async (c: CipherView) => { this.modal.close(); await this.ciphersComponent.refresh(); }); childComponent.onDeletedCipher.subscribe(async (c: CipherView) => { this.modal.close(); await this.ciphersComponent.refresh(); }); this.modal.onClosed.subscribe(() => { this.modal = null; }); return childComponent; } async viewEvents(cipher: CipherView) { if (this.modal != null) { this.modal.close(); } const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); this.modal = this.eventsModalRef.createComponent(factory).instance; const childComponent = this.modal.show( EntityEventsComponent, this.eventsModalRef); childComponent.name = cipher.name; childComponent.organizationId = this.organization.id; childComponent.entityId = cipher.id; childComponent.showUser = true; childComponent.entity = 'cipher'; this.modal.onClosed.subscribe(() => { this.modal = null; }); } private clearFilters() { this.collectionId = null; this.type = null; } private go(queryParams: any = null) { if (queryParams == null) { queryParams = { type: this.type, collectionId: this.collectionId, }; } this.router.navigate([], { relativeTo: this.route, queryParams: queryParams, replaceUrl: true, }); } }