diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 027b6618..b0672fdc 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -2,11 +2,9 @@ import { BodyOutputType, Toast, ToasterConfig, - ToasterContainerComponent, ToasterService, } from 'angular2-toaster'; import { Angulartics2 } from 'angulartics2'; -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { Component, @@ -16,7 +14,6 @@ import { SecurityContext, Type, ViewChild, - ViewContainerRef, } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { Router } from '@angular/router'; @@ -52,10 +49,16 @@ import { UserService } from 'jslib/abstractions/user.service'; import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service'; import { ConstantsService } from 'jslib/services/constants.service'; -import { NativeMessagingService } from '../services/nativeMessaging.service'; + +import { CipherType } from 'jslib/enums/cipherType'; + +import { ExportComponent } from './vault/export.component'; +import { FolderAddEditComponent } from './vault/folder-add-edit.component'; +import { PasswordGeneratorComponent } from './vault/password-generator.component'; const BroadcasterSubscriptionId = 'AppComponent'; const IdleTimeout = 60000 * 10; // 10 minutes +const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours @Component({ selector: 'app-root', @@ -65,12 +68,18 @@ const IdleTimeout = 60000 * 10; // 10 minutes + + + `, }) export class AppComponent implements OnInit { @ViewChild('settings', { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef; @ViewChild('premium', { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef; @ViewChild('passwordHistory', { read: ViewContainerRef, static: true }) passwordHistoryRef: ViewContainerRef; + @ViewChild('exportVault', { read: ViewContainerRef, static: true }) exportVaultModalRef: ViewContainerRef; + @ViewChild('appFolderAddEdit', { read: ViewContainerRef, static: true }) folderAddEditModalRef: ViewContainerRef; + @ViewChild('appPasswordGenerator', { read: ViewContainerRef, static: true }) passwordGeneratorModalRef: ViewContainerRef; toasterConfig: ToasterConfig = new ToasterConfig({ showCloseButton: true, @@ -84,8 +93,7 @@ export class AppComponent implements OnInit { private idleTimer: number = null; private isIdle = false; - constructor(private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics, - private broadcasterService: BroadcasterService, private userService: UserService, + constructor(private broadcasterService: BroadcasterService, private userService: UserService, private tokenService: TokenService, private folderService: FolderService, private settingsService: SettingsService, private syncService: SyncService, private passwordGenerationService: PasswordGenerationService, private cipherService: CipherService, @@ -98,7 +106,7 @@ export class AppComponent implements OnInit { private searchService: SearchService, private notificationsService: NotificationsService, private platformUtilsService: PlatformUtilsService, private systemService: SystemService, private stateService: StateService, private eventService: EventService, - private policyService: PolicyService, private nativeMessagingService: NativeMessagingService) { } + private policyService: PolicyService) { } ngOnInit() { this.ngZone.runOutsideAngular(() => { @@ -173,7 +181,7 @@ export class AppComponent implements OnInit { this.i18nService.t('fingerprintPhrase'), this.i18nService.t('learnMore'), this.i18nService.t('close')); if (result) { - this.platformUtilsService.launchUri( + this.platformUtilsService.launchUri( 'https://help.bitwarden.com/article/fingerprint-phrase/'); } break; @@ -205,7 +213,55 @@ export class AppComponent implements OnInit { this.openModal(PremiumComponent, this.premiumRef); } break; + case 'syncVault': + try { + await this.syncService.fullSync(true, true); + this.toasterService.popAsync('success', null, this.i18nService.t('syncingComplete')); + this.analytics.eventTrack.next({ action: 'Synced Full' }); + } catch { + this.toasterService.popAsync('error', null, this.i18nService.t('syncingFailed')); + } + break; + case 'checkSyncVault': + try { + const lastSync = await this.syncService.getLastSync(); + let lastSyncAgo = SyncInterval + 1; + if (lastSync != null) { + lastSyncAgo = new Date().getTime() - lastSync.getTime(); + } + + if (lastSyncAgo >= SyncInterval) { + await this.syncService.fullSync(false); + } + } catch { } + this.messagingService.send('scheduleNextSync'); + break; + case 'exportVault': + await this.openExportVault(); + break; + case 'newLogin': + this.routeToVault('add', CipherType.Login); + break; + case 'newCard': + this.routeToVault('add', CipherType.Card); + break; + case 'newIdentity': + this.routeToVault('add', CipherType.Identity); + break; + case 'newSecureNote': + this.routeToVault('add', CipherType.SecureNote); + break; default: + break; + case 'newFolder': + await this.addFolder(); + break; + case 'openPasswordGenerator': + // openPasswordGenerator has extended functionality if called in the vault + if (!this.router.url.includes('vault')) { + await this.openPasswordGenerator(); + } + break; } }); }); @@ -215,6 +271,59 @@ export class AppComponent implements OnInit { this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); } + async openExportVault() { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.exportVaultModalRef.createComponent(factory).instance; + const childComponent = this.modal.show(ExportComponent, this.exportVaultModalRef); + + childComponent.onSaved.subscribe(() => { + this.modal.close(); + }); + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); + } + + async addFolder() { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.folderAddEditModalRef.createComponent(factory).instance; + const childComponent = this.modal.show( + FolderAddEditComponent, this.folderAddEditModalRef, true, comp => comp.folderId = null); + + childComponent.onSavedFolder.subscribe(async () => { + this.modal.close(); + this.syncService.fullSync(false); + }); + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); + } + + async openPasswordGenerator() { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.passwordGeneratorModalRef.createComponent(factory).instance; + this.modal.show(PasswordGeneratorComponent, + this.passwordGeneratorModalRef, true, comp => comp.showSelect = false); + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); + } + private async updateAppMenu() { this.messagingService.send('updateAppMenu', { isAuthenticated: await this.userService.isAuthenticated(), @@ -328,4 +437,16 @@ export class AppComponent implements OnInit { } this.toasterService.popAsync(toast); } + + private routeToVault(action: string, cipherType: CipherType) { + if (!this.router.url.includes('vault')) { + this.router.navigate(['/vault'], { + queryParams: { + action: action, + addType: cipherType, + }, + replaceUrl: true, + }); + } + } } diff --git a/src/app/vault/vault.component.html b/src/app/vault/vault.component.html index e6740c1d..9c7cef1e 100644 --- a/src/app/vault/vault.component.html +++ b/src/app/vault/vault.component.html @@ -38,4 +38,3 @@ - diff --git a/src/app/vault/vault.component.ts b/src/app/vault/vault.component.ts index 34e543f4..4f8980f3 100644 --- a/src/app/vault/vault.component.ts +++ b/src/app/vault/vault.component.ts @@ -49,7 +49,6 @@ import { SyncService } from 'jslib/abstractions/sync.service'; import { TotpService } from 'jslib/abstractions/totp.service'; import { UserService } from 'jslib/abstractions/user.service'; -const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours const BroadcasterSubscriptionId = 'VaultComponent'; @Component({ @@ -63,11 +62,10 @@ export class VaultComponent implements OnInit, OnDestroy { @ViewChild(GroupingsComponent, { static: true }) groupingsComponent: GroupingsComponent; @ViewChild('passwordGenerator', { read: ViewContainerRef, static: true }) passwordGeneratorModalRef: ViewContainerRef; @ViewChild('attachments', { read: ViewContainerRef, static: true }) attachmentsModalRef: ViewContainerRef; - @ViewChild('folderAddEdit', { read: ViewContainerRef, static: true }) folderAddEditModalRef: ViewContainerRef; @ViewChild('passwordHistory', { read: ViewContainerRef, static: true }) passwordHistoryModalRef: ViewContainerRef; - @ViewChild('exportVault', { read: ViewContainerRef, static: true }) exportVaultModalRef: ViewContainerRef; @ViewChild('share', { read: ViewContainerRef, static: true }) shareModalRef: ViewContainerRef; @ViewChild('collections', { read: ViewContainerRef, static: true }) collectionsModalRef: ViewContainerRef; + @ViewChild('folderAddEdit', { read: ViewContainerRef, static: true }) folderAddEditModalRef: ViewContainerRef; action: string; cipherId: string = null; @@ -111,9 +109,6 @@ export class VaultComponent implements OnInit, OnDestroy { case 'newSecureNote': await this.addCipher(CipherType.SecureNote); break; - case 'newFolder': - await this.addFolder(); - break; case 'focusSearch': (document.querySelector('#search') as HTMLInputElement).select(); detectChanges = false; @@ -121,33 +116,6 @@ export class VaultComponent implements OnInit, OnDestroy { case 'openPasswordGenerator': await this.openPasswordGenerator(false); break; - case 'exportVault': - await this.openExportVault(); - break; - case 'syncVault': - try { - await this.syncService.fullSync(true, true); - this.toasterService.popAsync('success', null, this.i18nService.t('syncingComplete')); - this.analytics.eventTrack.next({ action: 'Synced Full' }); - } catch { - this.toasterService.popAsync('error', null, this.i18nService.t('syncingFailed')); - } - break; - case 'checkSyncVault': - try { - const lastSync = await this.syncService.getLastSync(); - let lastSyncAgo = SyncInterval + 1; - if (lastSync != null) { - lastSyncAgo = new Date().getTime() - lastSync.getTime(); - } - - if (lastSyncAgo >= SyncInterval) { - await this.syncService.fullSync(false); - } - } catch { } - - this.messagingService.send('scheduleNextSync'); - break; case 'syncCompleted': await this.load(); break; @@ -230,7 +198,28 @@ export class VaultComponent implements OnInit, OnDestroy { await this.viewCipher(cipherView); } } else if (params.action === 'add') { - await this.addCipher(); + switch (params.addType) { + case 'Login': + case '1': + this.addType = CipherType.Login; + break; + case 'SecureNote': + case '2': + this.addType = CipherType.SecureNote; + break; + case 'Card': + case '3': + this.addType = CipherType.Card; + break; + case 'Identity': + case '4': + this.addType = CipherType.Identity; + break; + default: + this.addType = CipherType.Login; + break; + } + this.addCipher(this.addType); } if (params.deleted) { @@ -239,7 +228,7 @@ export class VaultComponent implements OnInit, OnDestroy { } else if (params.favorites) { this.groupingsComponent.selectedFavorites = true; await this.filterFavorites(); - } else if (params.type) { + } else if (params.type && params.action !== 'add') { const t = parseInt(params.type, null); this.groupingsComponent.selectedType = t; await this.filterCipherType(t); @@ -593,42 +582,8 @@ export class VaultComponent implements OnInit, OnDestroy { }); } - async openExportVault() { - if (this.modal != null) { - this.modal.close(); - } - - const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); - this.modal = this.exportVaultModalRef.createComponent(factory).instance; - const childComponent = this.modal.show(ExportComponent, this.exportVaultModalRef); - - childComponent.onSaved.subscribe(() => { - this.modal.close(); - }); - - this.modal.onClosed.subscribe(() => { - this.modal = null; - }); - } - async addFolder() { - if (this.modal != null) { - this.modal.close(); - } - - const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); - this.modal = this.folderAddEditModalRef.createComponent(factory).instance; - const childComponent = this.modal.show( - FolderAddEditComponent, this.folderAddEditModalRef, true, comp => comp.folderId = null); - - childComponent.onSavedFolder.subscribe(async (folder: FolderView) => { - this.modal.close(); - await this.groupingsComponent.loadFolders(); - }); - - this.modal.onClosed.subscribe(() => { - this.modal = null; - }); + this.messagingService.send('newFolder'); } async editFolder(folderId: string) {