From 26659e528b771030f62c20f86f40be245c43e1e7 Mon Sep 17 00:00:00 2001 From: Hinton Date: Tue, 14 Sep 2021 17:07:57 +0200 Subject: [PATCH] [feature] Implement account switching --- src/app/accounts/lock.component.ts | 10 +- src/app/accounts/login.component.html | 10 +- src/app/accounts/login.component.ts | 13 +- src/app/accounts/premium.component.ts | 6 +- src/app/accounts/set-password.component.ts | 8 +- src/app/accounts/settings.component.ts | 96 +++++----- src/app/accounts/sso.component.ts | 16 +- src/app/accounts/two-factor.component.ts | 8 +- .../update-temp-password.component.ts | 6 +- src/app/app-routing.module.ts | 3 - src/app/app.component.ts | 54 +++--- src/app/app.module.ts | 22 ++- .../layout/account-switcher.component.html | 22 +++ src/app/layout/account-switcher.component.ts | 52 ++++++ src/app/layout/header.component.html | 4 + src/app/layout/header.component.ts | 11 ++ src/app/layout/search/search-bar.service.ts | 39 ++++ src/app/layout/search/search.component.html | 4 + src/app/layout/search/search.component.ts | 24 +++ src/app/send/add-edit.component.ts | 6 +- src/app/send/send.component.html | 7 - src/app/send/send.component.ts | 11 +- src/app/services.module.ts | 71 +++---- src/app/vault/add-edit.component.ts | 7 +- src/app/vault/attachments.component.ts | 6 +- src/app/vault/ciphers.component.html | 7 - src/app/vault/ciphers.component.ts | 13 ++ src/app/vault/groupings.component.ts | 6 +- src/app/vault/share.component.ts | 6 +- src/app/vault/vault.component.ts | 29 +-- src/app/vault/view.component.ts | 6 +- src/locales/en/messages.json | 3 + src/main.ts | 32 ++-- src/main/menu.main.ts | 4 +- src/main/messaging.main.ts | 12 +- src/main/powerMonitor.main.ts | 6 +- src/scss/header.scss | 175 ++++++++++++++++++ src/scss/misc.scss | 4 + src/scss/pages.scss | 21 +++ src/scss/styles.scss | 4 +- src/scss/vault.scss | 72 ++----- src/services/nativeMessaging.service.ts | 14 +- 42 files changed, 602 insertions(+), 328 deletions(-) create mode 100644 src/app/layout/account-switcher.component.html create mode 100644 src/app/layout/account-switcher.component.ts create mode 100644 src/app/layout/header.component.html create mode 100644 src/app/layout/header.component.ts create mode 100644 src/app/layout/search/search-bar.service.ts create mode 100644 src/app/layout/search/search.component.html create mode 100644 src/app/layout/search/search.component.ts create mode 100644 src/scss/header.scss diff --git a/src/app/accounts/lock.component.ts b/src/app/accounts/lock.component.ts index 478772c5..cd7c63d9 100644 --- a/src/app/accounts/lock.component.ts +++ b/src/app/accounts/lock.component.ts @@ -9,7 +9,6 @@ import { } from '@angular/router'; import { ipcRenderer } from 'electron'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { EnvironmentService } from 'jslib-common/abstractions/environment.service'; @@ -24,8 +23,6 @@ import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; import { LockComponent as BaseLockComponent } from 'jslib-angular/components/lock.component'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - const BroadcasterSubscriptionId = 'LockComponent'; @Component({ @@ -41,15 +38,14 @@ export class LockComponent extends BaseLockComponent implements OnDestroy { environmentService: EnvironmentService, stateService: StateService, apiService: ApiService, private route: ActivatedRoute, private broadcasterService: BroadcasterService, private ngZone: NgZone, - logService: LogService, activeAccount: ActiveAccountService) { + logService: LogService) { super(router, i18nService, platformUtilsService, messagingService, cryptoService, - vaultTimeoutService, environmentService, stateService, apiService, logService, - activeAccount); + vaultTimeoutService, environmentService, stateService, apiService, logService); } async ngOnInit() { await super.ngOnInit(); - const autoPromptBiometric = !await this.activeAccount.getInformation(StorageKey.NoAutoPromptBiometrics); + const autoPromptBiometric = !await this.stateService.getNoAutoPromptBiometrics(); this.route.queryParams.subscribe(params => { if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) { diff --git a/src/app/accounts/login.component.html b/src/app/accounts/login.component.html index 1a9516e3..7076ad62 100644 --- a/src/app/accounts/login.component.html +++ b/src/app/accounts/login.component.html @@ -1,3 +1,9 @@ +
diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index ed46547d..a12b18a0 100644 --- a/src/app/accounts/login.component.ts +++ b/src/app/accounts/login.component.ts @@ -10,7 +10,6 @@ import { Router } from '@angular/router'; import { EnvironmentComponent } from './environment.component'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service'; import { EnvironmentService } from 'jslib-common/abstractions/environment.service'; @@ -20,7 +19,6 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StateService } from 'jslib-common/abstractions/state.service'; -import { StorageService } from 'jslib-common/abstractions/storage.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; @@ -28,8 +26,6 @@ import { ModalService } from 'jslib-angular/services/modal.service'; import { LoginComponent as BaseLoginComponent } from 'jslib-angular/components/login.component'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - const BroadcasterSubscriptionId = 'LoginComponent'; @Component({ @@ -47,12 +43,11 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy { syncService: SyncService, private modalService: ModalService, platformUtilsService: PlatformUtilsService, stateService: StateService, environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService, - cryptoFunctionService: CryptoFunctionService, storageService: StorageService, - private broadcasterService: BroadcasterService, private ngZone: NgZone, - private messagingService: MessagingService, logService: LogService, - activeAccount: ActiveAccountService) { + cryptoFunctionService: CryptoFunctionService, private broadcasterService: BroadcasterService, + private ngZone: NgZone, private messagingService: MessagingService, + logService: LogService) { super(authService, router, platformUtilsService, i18nService, stateService, environmentService, - passwordGenerationService, cryptoFunctionService, storageService, logService, activeAccount); + passwordGenerationService, cryptoFunctionService, logService); super.onSuccessfulLogin = () => { return syncService.fullSync(true); }; diff --git a/src/app/accounts/premium.component.ts b/src/app/accounts/premium.component.ts index 3e44eb79..26681fdb 100644 --- a/src/app/accounts/premium.component.ts +++ b/src/app/accounts/premium.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { PremiumComponent as BasePremiumComponent } from 'jslib-angular/components/premium.component'; @@ -15,7 +15,7 @@ import { PremiumComponent as BasePremiumComponent } from 'jslib-angular/componen export class PremiumComponent extends BasePremiumComponent { constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService, apiService: ApiService, logService: LogService, - activeAccount: ActiveAccountService) { - super(i18nService, platformUtilsService, apiService, logService, activeAccount); + stateService: StateService) { + super(i18nService, platformUtilsService, apiService, logService, stateService); } } diff --git a/src/app/accounts/set-password.component.ts b/src/app/accounts/set-password.component.ts index 162648aa..25360f6c 100644 --- a/src/app/accounts/set-password.component.ts +++ b/src/app/accounts/set-password.component.ts @@ -9,7 +9,6 @@ import { Router, } from '@angular/router'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; @@ -17,10 +16,9 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { PolicyService } from 'jslib-common/abstractions/policy.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; const BroadcasterSubscriptionId = 'SetPasswordComponent'; @@ -40,10 +38,10 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On policyService: PolicyService, router: Router, syncService: SyncService, route: ActivatedRoute, private broadcasterService: BroadcasterService, private ngZone: NgZone, - activeAccount: ActiveAccountService) { + stateService: StateService) { super(i18nService, cryptoService, messagingService, passwordGenerationService, platformUtilsService, policyService, router, apiService, syncService, route, - activeAccount); + stateService); } get masterPasswordScoreWidth() { diff --git a/src/app/accounts/settings.component.ts b/src/app/accounts/settings.component.ts index 382752de..2ca6c019 100644 --- a/src/app/accounts/settings.component.ts +++ b/src/app/accounts/settings.component.ts @@ -8,13 +8,11 @@ import { debounceTime } from 'rxjs/operators'; import { DeviceType } from 'jslib-common/enums/deviceType'; import { ThemeType } from 'jslib-common/enums/themeType'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StateService } from 'jslib-common/abstractions/state.service'; -import { StorageService } from 'jslib-common/abstractions/storage.service'; import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service'; import { ModalService } from 'jslib-angular/services/modal.service'; @@ -24,7 +22,7 @@ import { SetPinComponent } from '../components/set-pin.component'; import { Utils } from 'jslib-common/misc/utils'; import { isWindowsStore } from 'jslib-electron/utils'; -import { StorageKey } from 'jslib-common/enums/storageKey'; +import { StorageLocation } from 'jslib-common/enums/storageLocation'; @Component({ selector: 'app-settings', @@ -73,8 +71,7 @@ export class SettingsComponent implements OnInit { constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private vaultTimeoutService: VaultTimeoutService, private stateService: StateService, private messagingService: MessagingService, private cryptoService: CryptoService, - private modalService: ModalService, private activeAccount: ActiveAccountService, - private storageService: StorageService) { + private modalService: ModalService) { const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; // Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622 @@ -153,31 +150,29 @@ export class SettingsComponent implements OnInit { async ngOnInit() { this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop; - this.vaultTimeout.setValue(await this.activeAccount.getInformation(StorageKey.VaultTimeout) ?? 1); - this.vaultTimeoutAction = await this.activeAccount.getInformation(StorageKey.VaultTimeoutAction) ?? 'lock'; + this.vaultTimeout.setValue(await this.stateService.getVaultTimeout() ?? 1); + this.vaultTimeoutAction = await this.stateService.getVaultTimeoutAction() ?? 'lock'; const pinSet = await this.vaultTimeoutService.isPinLockSet(); this.pin = pinSet[0] || pinSet[1]; - this.disableFavicons = await this.storageService.get(StorageKey.DisableFavicon) ?? false; - this.enableBrowserIntegration = await this.activeAccount.getInformation( - StorageKey.EnableBrowserIntegration); - this.enableBrowserIntegrationFingerprint = await this.activeAccount.getInformation(StorageKey.EnableBrowserIntegrationFingerprint); - this.enableMinToTray = await this.activeAccount.getInformation(StorageKey.EnableMinimizeToTrayKey); - this.enableCloseToTray = await this.activeAccount.getInformation(StorageKey.EnableCloseToTrayKey); - this.enableTray = await this.activeAccount.getInformation(StorageKey.EnableTrayKey); - this.startToTray = await this.activeAccount.getInformation(StorageKey.EnableStartToTrayKey); - this.locale = await this.activeAccount.getInformation(StorageKey.Locale); - this.theme = await this.activeAccount.getInformation(StorageKey.Theme); - this.clearClipboard = await this.activeAccount.getInformation(StorageKey.ClearClipboard); - this.minimizeOnCopyToClipboard = await this.activeAccount.getInformation( - StorageKey.MinimizeOnCopyToClipboardKey); + this.disableFavicons = await this.stateService.getDisableFavicon() ?? false; + this.enableBrowserIntegration = await this.stateService.getEnableBrowserIntegration() ?? false; + this.enableBrowserIntegrationFingerprint = await this.stateService.getEnableBrowserIntegrationFingerprint() ?? false; + this.enableMinToTray = await this.stateService.getEnableMinimizeToTray() ?? false; + this.enableCloseToTray = await this.stateService.getEnableCloseToTray() ?? false; + this.enableTray = await this.stateService.getEnableTray() ?? false; + this.startToTray = await this.stateService.getEnableStartToTray() ?? false; + this.locale = await this.stateService.getLocale() ?? 'en'; + this.theme = await this.stateService.getTheme() ?? null; + this.clearClipboard = await this.stateService.getClearClipboard() ?? 0; + this.minimizeOnCopyToClipboard = await this.stateService.getMinimizeOnCopyToClipboard() ?? false; this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); - this.biometric = await this.vaultTimeoutService.isBiometricLockSet(); - this.biometricText = await this.activeAccount.getInformation(StorageKey.BiometricText); - this.noAutoPromptBiometrics = await this.activeAccount.getInformation(StorageKey.NoAutoPromptBiometrics); - this.noAutoPromptBiometricsText = await this.activeAccount.getInformation(StorageKey.NoAutoPromptBiometricsText); - this.alwaysShowDock = await this.activeAccount.getInformation(StorageKey.AlwaysShowDock); + this.biometric = await this.vaultTimeoutService.isBiometricLockSet() ?? false; + this.biometricText = await this.stateService.getBiometricText(); + this.noAutoPromptBiometrics = await this.stateService.getNoAutoPromptBiometrics() ?? false; + this.noAutoPromptBiometricsText = await this.stateService.getNoAutoPromptBiometricsText(); + this.alwaysShowDock = await this.stateService.getAlwaysShowDock() ?? false; this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; - this.openAtLogin = await this.activeAccount.getInformation(StorageKey.OpenAtLogin); + this.openAtLogin = await this.stateService.getOpenAtLogin() ?? false; } async saveVaultTimeoutOptions() { @@ -233,13 +228,13 @@ export class SettingsComponent implements OnInit { return; } if (this.biometric) { - await this.activeAccount.saveInformation(StorageKey.BiometricUnlock, true); + await this.stateService.setBiometricUnlock(true); } else { - await this.activeAccount.removeInformation(StorageKey.BiometricUnlock); - await this.activeAccount.removeInformation(StorageKey.NoAutoPromptBiometrics); + await this.stateService.setBiometricUnlock(null); + await this.stateService.setNoAutoPromptBiometrics(null); this.noAutoPromptBiometrics = false; } - this.vaultTimeoutService.biometricLocked = false; + this.stateService.setBiometricLocked(false); await this.cryptoService.toggleKey(); } @@ -249,29 +244,29 @@ export class SettingsComponent implements OnInit { } if (this.noAutoPromptBiometrics) { - await this.activeAccount.saveInformation(StorageKey.NoAutoPromptBiometrics, true); + await this.stateService.setNoAutoPromptBiometrics(true); } else { - await this.activeAccount.removeInformation(StorageKey.NoAutoPromptBiometrics); + await this.stateService.setNoAutoPromptBiometrics(null); } } async saveFavicons() { - await this.activeAccount.saveInformation(StorageKey.DisableFavicon, this.disableFavicons); - await this.stateService.save(StorageKey.DisableFavicon, this.disableFavicons); + await this.stateService.setDisableFavicon(this.disableFavicons); + await this.stateService.setDisableFavicon(this.disableFavicons, { storageLocation: StorageLocation.Disk }); this.messagingService.send('refreshCiphers'); } async saveMinToTray() { - await this.activeAccount.saveInformation(StorageKey.EnableMinimizeToTrayKey, this.enableMinToTray); + await this.stateService.setEnableMinimizeToTray(this.enableMinToTray); } async saveCloseToTray() { if (this.requireEnableTray) { this.enableTray = true; - await this.activeAccount.saveInformation(StorageKey.EnableTrayKey, this.enableTray); + await this.stateService.setEnableTray(this.enableTray); } - await this.activeAccount.saveInformation(StorageKey.EnableCloseToTrayKey, this.enableCloseToTray); + await this.stateService.setEnableCloseToTray(this.enableCloseToTray); } async saveTray() { @@ -282,9 +277,9 @@ export class SettingsComponent implements OnInit { if (confirm) { this.startToTray = false; - await this.activeAccount.saveInformation(StorageKey.EnableStartToTrayKey, this.startToTray); + await this.stateService.setEnableStartToTray(this.startToTray); this.enableCloseToTray = false; - await this.activeAccount.saveInformation(StorageKey.EnableCloseToTrayKey, this.enableCloseToTray); + await this.stateService.setEnableCloseToTray(this.enableCloseToTray); } else { this.enableTray = true; } @@ -292,43 +287,42 @@ export class SettingsComponent implements OnInit { return; } - await this.activeAccount.saveInformation(StorageKey.EnableTrayKey, this.enableTray); + await this.stateService.setEnableTray(this.enableTray); this.messagingService.send(this.enableTray ? 'showTray' : 'removeTray'); } async saveStartToTray() { if (this.requireEnableTray) { this.enableTray = true; - await this.activeAccount.saveInformation(StorageKey.EnableTrayKey, this.enableTray); + await this.stateService.setEnableTray(this.enableTray); } - await this.activeAccount.saveInformation(StorageKey.EnableStartToTrayKey, this.startToTray); + await this.stateService.setEnableStartToTray(this.startToTray); } async saveLocale() { - await this.activeAccount.saveInformation(StorageKey.Locale, this.locale); + await this.stateService.setLocale(this.locale); } async saveTheme() { - await this.activeAccount.saveInformation(StorageKey.Theme, this.theme); - await this.storageService.save(`global.${StorageKey.Theme}`, this.theme); + await this.stateService.setTheme(this.theme); window.setTimeout(() => window.location.reload(), 200); } async saveMinOnCopyToClipboard() { - await this.activeAccount.saveInformation(StorageKey.MinimizeOnCopyToClipboardKey, this.minimizeOnCopyToClipboard); + await this.stateService.setMinimizeOnCopyToClipboard(this.minimizeOnCopyToClipboard); } async saveClearClipboard() { - await this.activeAccount.saveInformation(StorageKey.ClearClipboard, this.clearClipboard); + await this.stateService.setClearClipboard(this.clearClipboard); } async saveAlwaysShowDock() { - await this.activeAccount.saveInformation(StorageKey.AlwaysShowDock, this.alwaysShowDock); + await this.stateService.setAlwaysShowDock(this.alwaysShowDock); } async saveOpenAtLogin() { - this.activeAccount.saveInformation(StorageKey.OpenAtLogin, this.openAtLogin); + this.stateService.setOpenAtLogin(this.openAtLogin); this.messagingService.send(this.openAtLogin ? 'addOpenAtLogin' : 'removeOpenAtLogin'); } @@ -351,7 +345,7 @@ export class SettingsComponent implements OnInit { return; } - await this.activeAccount.saveInformation(StorageKey.EnableBrowserIntegration, this.enableBrowserIntegration); + await this.stateService.setEnableBrowserIntegration(this.enableBrowserIntegration); this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration'); if (!this.enableBrowserIntegration) { @@ -361,6 +355,6 @@ export class SettingsComponent implements OnInit { } async saveBrowserIntegrationFingerprint() { - await this.activeAccount.saveInformation(StorageKey.EnableBrowserIntegrationFingerprint, this.enableBrowserIntegrationFingerprint); + await this.stateService.setEnableBrowserIntegrationFingerprint(this.enableBrowserIntegrationFingerprint); } } diff --git a/src/app/accounts/sso.component.ts b/src/app/accounts/sso.component.ts index feeb3adf..615ba612 100644 --- a/src/app/accounts/sso.component.ts +++ b/src/app/accounts/sso.component.ts @@ -5,7 +5,6 @@ import { Router, } from '@angular/router'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service'; @@ -15,7 +14,6 @@ import { LogService } from 'jslib-common/abstractions/log.service'; import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StateService } from 'jslib-common/abstractions/state.service'; -import { StorageService } from 'jslib-common/abstractions/storage.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component'; @@ -27,14 +25,12 @@ import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.c export class SsoComponent extends BaseSsoComponent { constructor(authService: AuthService, router: Router, i18nService: I18nService, syncService: SyncService, route: ActivatedRoute, - storageService: StorageService, stateService: StateService, - platformUtilsService: PlatformUtilsService, apiService: ApiService, - cryptoFunctionService: CryptoFunctionService, environmentService: EnvironmentService, - passwordGenerationService: PasswordGenerationService, logService: LogService, - activeAccount: ActiveAccountService) { - super(authService, router, i18nService, route, storageService, stateService, platformUtilsService, - apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService, - activeAccount); + stateService: StateService, platformUtilsService: PlatformUtilsService, + apiService: ApiService, cryptoFunctionService: CryptoFunctionService, + environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService, + logService: LogService) { + super(authService, router, i18nService, route, stateService, platformUtilsService, + apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService); super.onSuccessfulLogin = () => { return syncService.fullSync(true); }; diff --git a/src/app/accounts/two-factor.component.ts b/src/app/accounts/two-factor.component.ts index 5a6c575e..2bacb24f 100644 --- a/src/app/accounts/two-factor.component.ts +++ b/src/app/accounts/two-factor.component.ts @@ -13,7 +13,6 @@ import { TwoFactorOptionsComponent } from './two-factor-options.component'; import { TwoFactorProviderType } from 'jslib-common/enums/twoFactorProviderType'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { EnvironmentService } from 'jslib-common/abstractions/environment.service'; @@ -21,15 +20,12 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { StateService } from 'jslib-common/abstractions/state.service'; -import { StorageService } from 'jslib-common/abstractions/storage.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; import { ModalService } from 'jslib-angular/services/modal.service'; import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib-angular/components/two-factor.component'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - @Component({ selector: 'app-two-factor', templateUrl: 'two-factor.component.html', @@ -43,10 +39,10 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { i18nService: I18nService, apiService: ApiService, platformUtilsService: PlatformUtilsService, syncService: SyncService, environmentService: EnvironmentService, private modalService: ModalService, - stateService: StateService, storageService: StorageService, route: ActivatedRoute, + stateService: StateService, route: ActivatedRoute, logService: LogService) { super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService, - stateService, storageService, route, logService); + stateService, route, logService); super.onSuccessfulLogin = () => { return syncService.fullSync(true); }; diff --git a/src/app/accounts/update-temp-password.component.ts b/src/app/accounts/update-temp-password.component.ts index f3fd8a3d..8a33e70f 100644 --- a/src/app/accounts/update-temp-password.component.ts +++ b/src/app/accounts/update-temp-password.component.ts @@ -1,6 +1,5 @@ import { Component } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; @@ -9,6 +8,7 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { PolicyService } from 'jslib-common/abstractions/policy.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from 'jslib-angular/components/update-temp-password.component'; @@ -58,8 +58,8 @@ export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent passwordGenerationService: PasswordGenerationService, policyService: PolicyService, cryptoService: CryptoService, messagingService: MessagingService, apiService: ApiService, syncService: SyncService, - logService: LogService, activeAccount: ActiveAccountService) { + logService: LogService, stateService: StateService) { super(i18nService, platformUtilsService, passwordGenerationService, policyService, cryptoService, - messagingService, apiService, activeAccount, syncService, logService); + messagingService, apiService, stateService, syncService, logService); } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 7346ba69..dac64540 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -6,7 +6,6 @@ import { import { AuthGuardService } from 'jslib-angular/services/auth-guard.service'; import { LockGuardService } from 'jslib-angular/services/lock-guard.service'; -import { UnauthGuardService } from 'jslib-angular/services/unauth-guard.service'; import { HintComponent } from './accounts/hint.component'; import { LockComponent } from './accounts/lock.component'; @@ -31,8 +30,6 @@ const routes: Routes = [ { path: 'login', component: LoginComponent, - canActivate: [UnauthGuardService], - }, { path: '2fa', component: TwoFactorComponent }, { path: 'register', component: RegisterComponent }, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index df0bc2b8..17230f57 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -23,8 +23,6 @@ import { PasswordGeneratorHistoryComponent } from './vault/password-generator-hi import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; -import { AccountsManagementService } from 'jslib-common/abstractions/accountsManagement.service'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { AuthService } from 'jslib-common/abstractions/auth.service'; import { CipherService } from 'jslib-common/abstractions/cipher.service'; import { CollectionService } from 'jslib-common/abstractions/collection.service'; @@ -47,14 +45,15 @@ import { TokenService } from 'jslib-common/abstractions/token.service'; import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service'; import { CipherType } from 'jslib-common/enums/cipherType'; -import { StorageKey } from 'jslib-common/enums/storageKey'; -import { ModalRef } from 'jslib-angular/components/modal/modal.ref'; -import { ModalService } from 'jslib-angular/services/modal.service'; import { ExportComponent } from './vault/export.component'; import { FolderAddEditComponent } from './vault/folder-add-edit.component'; import { PasswordGeneratorComponent } from './vault/password-generator.component'; +import { ModalRef } from 'jslib-angular/components/modal/modal.ref'; + +import { ModalService } from 'jslib-angular/services/modal.service'; + const BroadcasterSubscriptionId = 'AppComponent'; const IdleTimeout = 60000 * 10; // 10 minutes const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours @@ -70,7 +69,8 @@ const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours - `, + +
`, }) export class AppComponent implements OnInit { @ViewChild('settings', { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef; @@ -94,20 +94,19 @@ export class AppComponent implements OnInit { private idleTimer: number = null; private isIdle = false; - constructor(private broadcasterService: BroadcasterService, private activeAccount: ActiveAccountService, - private tokenService: TokenService, private folderService: FolderService, - private settingsService: SettingsService, private syncService: SyncService, - private passwordGenerationService: PasswordGenerationService, private cipherService: CipherService, - private authService: AuthService, private router: Router, - private toasterService: ToasterService, private i18nService: I18nService, - private sanitizer: DomSanitizer, private ngZone: NgZone, - private vaultTimeoutService: VaultTimeoutService, private cryptoService: CryptoService, - private logService: LogService, private messagingService: MessagingService, - private collectionService: CollectionService, private searchService: SearchService, - private notificationsService: NotificationsService, private platformUtilsService: PlatformUtilsService, - private systemService: SystemService, private stateService: StateService, - private eventService: EventService, private policyService: PolicyService, - private modalService: ModalService, private accountsManagementService: AccountsManagementService) { } + constructor(private broadcasterService: BroadcasterService, private tokenService: TokenService, + private folderService: FolderService, private settingsService: SettingsService, + private syncService: SyncService, private passwordGenerationService: PasswordGenerationService, + private cipherService: CipherService, private authService: AuthService, + private router: Router, private toasterService: ToasterService, + private i18nService: I18nService, private sanitizer: DomSanitizer, + private ngZone: NgZone, private vaultTimeoutService: VaultTimeoutService, + private cryptoService: CryptoService, private logService: LogService, + private messagingService: MessagingService, private collectionService: CollectionService, + private searchService: SearchService, private notificationsService: NotificationsService, + private platformUtilsService: PlatformUtilsService, private systemService: SystemService, + private stateService: StateService, private eventService: EventService, + private policyService: PolicyService, private modalService: ModalService) { } ngOnInit() { this.ngZone.runOutsideAngular(() => { @@ -154,7 +153,7 @@ export class AppComponent implements OnInit { if (this.modal != null) { this.modal.close(); } - this.stateService.purge(); + await this.stateService.purge(); this.router.navigate(['lock']); this.notificationsService.updateConnection(); this.updateAppMenu(); @@ -176,7 +175,7 @@ export class AppComponent implements OnInit { break; case 'showFingerprintPhrase': const fingerprint = await this.cryptoService.getFingerprint( - this.activeAccount.userId); + await this.stateService.getUserId()); const result = await this.platformUtilsService.showDialog( this.i18nService.t('yourAccountsFingerprint') + ':\n' + fingerprint.join('-'), this.i18nService.t('fingerprintPhrase'), this.i18nService.t('learnMore'), @@ -328,14 +327,14 @@ export class AppComponent implements OnInit { private async updateAppMenu() { this.messagingService.send('updateAppMenu', { - isAuthenticated: this.activeAccount.isAuthenticated, + isAuthenticated: await this.stateService.getIsAuthenticated(), isLocked: await this.vaultTimeoutService.isLocked(), }); } private async logOut(expired: boolean) { await this.eventService.uploadEvents(); - const userId = this.activeAccount.userId; + const userId = await this.stateService.getUserId(); await Promise.all([ this.eventService.clearEvents(), @@ -348,12 +347,11 @@ export class AppComponent implements OnInit { this.collectionService.clear(userId), this.passwordGenerationService.clear(), this.vaultTimeoutService.clear(), - this.stateService.purge(), this.policyService.clear(userId), - this.accountsManagementService.remove(userId), + await this.stateService.purge(), ]); - this.vaultTimeoutService.biometricLocked = true; + await this.stateService.setBiometricLocked(true); this.searchService.clearIndex(); this.authService.logOut(async () => { if (expired) { @@ -371,7 +369,7 @@ export class AppComponent implements OnInit { } this.lastActivity = now; - this.activeAccount.saveInformation(StorageKey.LastActive, now); + await this.stateService.setLastActive(now); // Idle states if (this.isIdle) { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 641333f1..427686b6 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,18 +1,17 @@ -import 'zone.js/dist/zone'; - -import { ToasterModule } from 'angular2-toaster'; - -import { AppRoutingModule } from './app-routing.module'; -import { ServicesModule } from './services.module'; - import { A11yModule } from '@angular/cdk/a11y'; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { OverlayModule } from '@angular/cdk/overlay'; import { ScrollingModule } from '@angular/cdk/scrolling'; import { DatePipe } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToasterModule } from 'angular2-toaster'; +import 'zone.js/dist/zone'; + +import { AppRoutingModule } from './app-routing.module'; +import { ServicesModule } from './services.module'; import { AppComponent } from './app.component'; @@ -30,6 +29,7 @@ import { TwoFactorComponent } from './accounts/two-factor.component'; import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component'; import { VaultTimeoutInputComponent } from './accounts/vault-timeout-input.component'; +import { AvatarComponent } from 'jslib-angular/components/avatar.component'; import { CalloutComponent } from 'jslib-angular/components/callout.component'; import { IconComponent } from 'jslib-angular/components/icon.component'; @@ -69,7 +69,10 @@ import { AddEditComponent as SendAddEditComponent } from './send/add-edit.compon import { EffluxDatesComponent as SendEffluxDatesComponent } from './send/efflux-dates.component'; import { SendComponent } from './send/send.component'; +import { AccountSwitcherComponent } from './layout/account-switcher.component'; +import { HeaderComponent } from './layout/header.component'; import { NavComponent } from './layout/nav.component'; +import { SearchComponent } from './layout/search/search.component'; import { PasswordRepromptComponent } from './components/password-reprompt.component'; import { SetPinComponent } from './components/set-pin.component'; @@ -177,6 +180,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); ToasterModule.forRoot(), ScrollingModule, A11yModule, + OverlayModule, ], declarations: [ A11yTitleDirective, @@ -230,6 +234,10 @@ registerLocaleData(localeZhTw, 'zh-TW'); VaultTimeoutInputComponent, AddEditCustomFieldsComponent, ViewCustomFieldsComponent, + HeaderComponent, + AccountSwitcherComponent, + AvatarComponent, + SearchComponent, ], providers: [DatePipe], bootstrap: [AppComponent], diff --git a/src/app/layout/account-switcher.component.html b/src/app/layout/account-switcher.component.html new file mode 100644 index 00000000..b5b6f9f1 --- /dev/null +++ b/src/app/layout/account-switcher.component.html @@ -0,0 +1,22 @@ + + + + + diff --git a/src/app/layout/account-switcher.component.ts b/src/app/layout/account-switcher.component.ts new file mode 100644 index 00000000..5d5c3db2 --- /dev/null +++ b/src/app/layout/account-switcher.component.ts @@ -0,0 +1,52 @@ +import { + animate, + state, + style, + transition, + trigger, + } from '@angular/animations'; +import { Component, OnInit } from '@angular/core'; + +import { StateService } from 'jslib-common/abstractions/state.service'; +import { SyncService } from 'jslib-common/abstractions/sync.service'; + +import { Account } from 'jslib-common/models/domain/account'; + +@Component({ + selector: 'app-account-switcher', + templateUrl: 'account-switcher.component.html', + animations: [ + trigger('transformPanel', [ + state('void', style({ + opacity: 0, + })), + transition('void => open', animate('100ms linear', style({ + opacity: 1, + }))), + transition('* => void', animate('100ms linear', style({opacity: 0}))), + ]), + ], +}) +export class AccountSwitcherComponent implements OnInit { + isOpen: boolean = false; + activeAccountEmail: string; + + get accounts(): Record { + return this.stateService.accounts.getValue(); + } + + constructor(private stateService: StateService, private syncService: SyncService) {} + + async ngOnInit(): Promise { + this.activeAccountEmail = await this.stateService.getEmail(); + } + + toggle() { + this.isOpen = !this.isOpen; + } + + async switch(userId: string) { + await this.stateService.setActiveUser(userId); + await this.syncService.fullSync(true); + } +} diff --git a/src/app/layout/header.component.html b/src/app/layout/header.component.html new file mode 100644 index 00000000..e1639ef0 --- /dev/null +++ b/src/app/layout/header.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/src/app/layout/header.component.ts b/src/app/layout/header.component.ts new file mode 100644 index 00000000..e4c24815 --- /dev/null +++ b/src/app/layout/header.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +import { I18nService } from 'jslib-common/abstractions/i18n.service'; + +@Component({ + selector: 'app-header', + templateUrl: 'header.component.html', +}) +export class HeaderComponent { + constructor(private i18nService: I18nService) {} +} diff --git a/src/app/layout/search/search-bar.service.ts b/src/app/layout/search/search-bar.service.ts new file mode 100644 index 00000000..94caeadf --- /dev/null +++ b/src/app/layout/search/search-bar.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +export type SearchBarState = { + enabled: boolean; + placeholderText: string; +}; + +@Injectable() +export class SearchBarService { + + searchText = new BehaviorSubject(null); + + private _state = { + enabled: false, + placeholderText: '', + }; + + // tslint:disable-next-line:member-ordering + state = new BehaviorSubject(this._state); + + setEnabled(enabled: boolean) { + this._state.enabled = enabled; + this.updateState(); + } + + setPlaceholderText(placeholderText: string) { + this._state.placeholderText = placeholderText; + this.updateState(); + } + + setSearchText(value: string) { + this.searchText.next(value); + } + + private updateState() { + this.state.next(this._state); + } +} diff --git a/src/app/layout/search/search.component.html b/src/app/layout/search/search.component.html new file mode 100644 index 00000000..1ea3353b --- /dev/null +++ b/src/app/layout/search/search.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/layout/search/search.component.ts b/src/app/layout/search/search.component.ts new file mode 100644 index 00000000..6c82d273 --- /dev/null +++ b/src/app/layout/search/search.component.ts @@ -0,0 +1,24 @@ +import { Component } from '@angular/core'; +import { FormControl } from '@angular/forms'; + +import { SearchBarService, SearchBarState } from './search-bar.service'; + +@Component({ + selector: 'app-search', + templateUrl: 'search.component.html', +}) +export class SearchComponent { + + state: SearchBarState; + searchText: FormControl = new FormControl(null); + + constructor(private searchBarService: SearchBarService) { + this.searchBarService.state.subscribe(state => { + this.state = state; + }); + + this.searchText.valueChanges.subscribe(value => { + this.searchBarService.setSearchText(value); + }); + } +} diff --git a/src/app/send/add-edit.component.ts b/src/app/send/add-edit.component.ts index bcf95564..f1e57c1f 100644 --- a/src/app/send/add-edit.component.ts +++ b/src/app/send/add-edit.component.ts @@ -2,7 +2,6 @@ import { DatePipe } from '@angular/common'; import { Component } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { EnvironmentService } from 'jslib-common/abstractions/environment.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; @@ -10,6 +9,7 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { PolicyService } from 'jslib-common/abstractions/policy.service'; import { SendService } from 'jslib-common/abstractions/send.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/send/add-edit.component'; @@ -20,12 +20,12 @@ import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/componen export class AddEditComponent extends BaseAddEditComponent { constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService, datePipe: DatePipe, - sendService: SendService, activeAccount: ActiveAccountService, + sendService: SendService, stateService: StateService, messagingService: MessagingService, policyService: PolicyService, logService: LogService) { super(i18nService, platformUtilsService, environmentService, datePipe, sendService, messagingService, policyService, - logService, activeAccount); + logService, stateService); } async refresh() { diff --git a/src/app/send/send.component.html b/src/app/send/send.component.html index e4740c90..6a94c8a0 100644 --- a/src/app/send/send.component.html +++ b/src/app/send/send.component.html @@ -31,13 +31,6 @@
-
{ + this.searchText = searchText; + this.searchTextChanged(); + }); } async ngOnInit() { + this.searchBarService.setEnabled(true); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchSends')); + super.ngOnInit(); this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { this.ngZone.run(async () => { @@ -67,6 +75,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro ngOnDestroy() { this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.searchBarService.setEnabled(false); } addSend() { diff --git a/src/app/services.module.ts b/src/app/services.module.ts index f8700aa7..76e1ca4b 100644 --- a/src/app/services.module.ts +++ b/src/app/services.module.ts @@ -15,6 +15,7 @@ import { ElectronRendererStorageService } from 'jslib-electron/services/electron import { I18nService } from '../services/i18n.service'; import { NativeMessagingService } from '../services/nativeMessaging.service'; import { PasswordRepromptService } from '../services/passwordReprompt.service'; +import { SearchBarService } from './layout/search/search-bar.service'; import { AuthGuardService } from 'jslib-angular/services/auth-guard.service'; import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; @@ -23,8 +24,6 @@ import { ModalService } from 'jslib-angular/services/modal.service'; import { UnauthGuardService } from 'jslib-angular/services/unauth-guard.service'; import { ValidationService } from 'jslib-angular/services/validation.service'; -import { AccountsManagementService } from 'jslib-common/services/accountsManagement.service'; -import { ActiveAccountService } from 'jslib-common/services/activeAccount.service'; import { ApiService } from 'jslib-common/services/api.service'; import { AppIdService } from 'jslib-common/services/appId.service'; import { AuditService } from 'jslib-common/services/audit.service'; @@ -46,7 +45,6 @@ import { SearchService } from 'jslib-common/services/search.service'; import { SendService } from 'jslib-common/services/send.service'; import { SettingsService } from 'jslib-common/services/settings.service'; import { StateService } from 'jslib-common/services/state.service'; -import { StoreService } from 'jslib-common/services/store.service'; import { SyncService } from 'jslib-common/services/sync.service'; import { SystemService } from 'jslib-common/services/system.service'; import { TokenService } from 'jslib-common/services/token.service'; @@ -56,8 +54,6 @@ import { WebCryptoFunctionService } from 'jslib-common/services/webCryptoFunctio import { ElectronCryptoService } from 'jslib-electron/services/electronCrypto.service'; -import { AccountsManagementService as AccountsManagementServiceAbstraction } from 'jslib-common/abstractions/accountsManagement.service'; -import { ActiveAccountService as ActiveAccountServiceAbstraction } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService as ApiServiceAbstraction } from 'jslib-common/abstractions/api.service'; import { AuditService as AuditServiceAbstraction } from 'jslib-common/abstractions/audit.service'; import { AuthService as AuthServiceAbstraction } from 'jslib-common/abstractions/auth.service'; @@ -92,64 +88,61 @@ import { SystemService as SystemServiceAbstraction } from 'jslib-common/abstract import { TokenService as TokenServiceAbstraction } from 'jslib-common/abstractions/token.service'; import { TotpService as TotpServiceAbstraction } from 'jslib-common/abstractions/totp.service'; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib-common/abstractions/vaultTimeout.service'; -import { StorageKey } from 'jslib-common/enums/storageKey'; import { ThemeType } from 'jslib-common/enums/themeType'; const logService = new ElectronLogService(); const i18nService = new I18nService(window.navigator.language, './locales'); -const stateService = new StateService(); const broadcasterService = new BroadcasterService(); const messagingService = new ElectronRendererMessagingService(broadcasterService); const storageService: StorageServiceAbstraction = new ElectronRendererStorageService(); const secureStorageService: StorageServiceAbstraction = new ElectronRendererSecureStorageService(); -const storeService = new StoreService(storageService, secureStorageService); -const accountsManagementService: AccountsManagementServiceAbstraction = new AccountsManagementService(storageService, secureStorageService); -const activeAccount: ActiveAccountServiceAbstraction = new ActiveAccountService(accountsManagementService, storeService); -const platformUtilsService = new ElectronPlatformUtilsService(i18nService, messagingService, true, storageService, activeAccount); +const stateService = new StateService(storageService, secureStorageService, + logService); +const platformUtilsService = new ElectronPlatformUtilsService(i18nService, messagingService, true, stateService); const cryptoFunctionService: CryptoFunctionServiceAbstraction = new WebCryptoFunctionService(window, platformUtilsService); const cryptoService = new ElectronCryptoService(cryptoFunctionService, platformUtilsService, - logService, activeAccount); -const tokenService = new TokenService(activeAccount); + logService, stateService); +const tokenService = new TokenService(stateService); const appIdService = new AppIdService(storageService); -const environmentService = new EnvironmentService(activeAccount); +const environmentService = new EnvironmentService(stateService); const apiService = new ApiService(tokenService, platformUtilsService, environmentService, async (expired: boolean) => messagingService.send('logout', { expired: expired })); -const settingsService = new SettingsService(activeAccount); +const settingsService = new SettingsService(stateService); export let searchService: SearchService = null; const fileUploadService = new FileUploadService(logService, apiService); const cipherService = new CipherService(cryptoService, settingsService, apiService, - fileUploadService, i18nService, () => searchService, logService, activeAccount); + fileUploadService, i18nService, () => searchService, logService, stateService); const folderService = new FolderService(cryptoService, apiService, i18nService, - cipherService, activeAccount); -const collectionService = new CollectionService(cryptoService, i18nService, activeAccount); + cipherService, stateService); +const collectionService = new CollectionService(cryptoService, i18nService, stateService); searchService = new SearchService(cipherService, logService, i18nService); const sendService = new SendService(cryptoService, apiService, fileUploadService, i18nService, - cryptoFunctionService, activeAccount); -const organizationService = new OrganizationService(activeAccount); -const providerService: ProviderServiceAbstraction = new ProviderService(activeAccount); -const policyService = new PolicyService(activeAccount, organizationService, apiService); + cryptoFunctionService, stateService); +const organizationService = new OrganizationService(stateService); +const providerService: ProviderServiceAbstraction = new ProviderService(stateService); +const policyService = new PolicyService(stateService, organizationService, apiService); const vaultTimeoutService = new VaultTimeoutService(cipherService, folderService, collectionService, - cryptoService, platformUtilsService, messagingService, searchService, tokenService, policyService, activeAccount, null, + cryptoService, platformUtilsService, messagingService, searchService, tokenService, policyService, stateService, null, async () => messagingService.send('logout', { expired: false })); const syncService = new SyncService(apiService, settingsService, folderService, cipherService, cryptoService, collectionService, messagingService, policyService, sendService, logService, - async (expired: boolean) => messagingService.send('logout', { expired: expired }), activeAccount, organizationService, providerService); -const passwordGenerationService = new PasswordGenerationService(cryptoService, policyService, activeAccount); -const totpService = new TotpService(cryptoFunctionService, logService, activeAccount); + async (expired: boolean) => messagingService.send('logout', { expired: expired }), stateService, organizationService, providerService); +const passwordGenerationService = new PasswordGenerationService(cryptoService, policyService, stateService); +const totpService = new TotpService(cryptoFunctionService, logService, stateService); const containerService = new ContainerService(cryptoService); const authService = new AuthService(cryptoService, apiService, tokenService, appIdService, i18nService, - platformUtilsService, messagingService, vaultTimeoutService, logService, activeAccount, accountsManagementService); + platformUtilsService, messagingService, vaultTimeoutService, logService, cryptoFunctionService, stateService); const exportService = new ExportService(folderService, cipherService, apiService, cryptoService); const auditService = new AuditService(cryptoFunctionService, apiService); const notificationsService = new NotificationsService(syncService, appIdService, - apiService, vaultTimeoutService, environmentService, async () => messagingService.send('logout', { expired: true }), logService, activeAccount); -const eventService = new EventService(apiService, cipherService, activeAccount, logService, organizationService); -const systemService = new SystemService(vaultTimeoutService, messagingService, platformUtilsService, null, - activeAccount); + apiService, vaultTimeoutService, environmentService, async () => messagingService.send('logout', { expired: true }), logService, stateService); +const eventService = new EventService(apiService, cipherService, stateService, logService, organizationService); +const systemService = new SystemService(messagingService, platformUtilsService, + null, stateService); const nativeMessagingService = new NativeMessagingService(cryptoFunctionService, cryptoService, platformUtilsService, - logService, i18nService, messagingService, vaultTimeoutService, activeAccount); + logService, i18nService, messagingService, vaultTimeoutService, stateService); containerService.attachToGlobal(window); @@ -158,7 +151,7 @@ export function initFactory(): Function { await environmentService.setUrlsFromStorage(); syncService.fullSync(true); vaultTimeoutService.init(true); - const locale = await activeAccount.getInformation(StorageKey.Locale); + const locale = await stateService.getLocale(); await i18nService.init(locale); eventService.init(true); authService.init(); @@ -169,18 +162,15 @@ export function initFactory(): Function { const theme = await platformUtilsService.getEffectiveTheme(); htmlEl.classList.add('theme_' + theme); platformUtilsService.onDefaultSystemThemeChange(async sysTheme => { - const bwTheme = await activeAccount.getInformation(StorageKey.Theme); + const bwTheme = await stateService.getTheme(); if (bwTheme == null || bwTheme === ThemeType.System) { htmlEl.classList.remove('theme_' + ThemeType.Light, 'theme_' + ThemeType.Dark); htmlEl.classList.add('theme_' + sysTheme); } }); - stateService.save(StorageKey.DisableFavicon, - await storageService.get(StorageKey.DisableFavicon)); - let installAction = null; - const installedVersion = await activeAccount.getInformation(StorageKey.InstalledVersion); + const installedVersion = await stateService.getInstalledVersion(); const currentVersion = await platformUtilsService.getApplicationVersion(); if (installedVersion == null) { installAction = 'install'; @@ -189,7 +179,7 @@ export function initFactory(): Function { } if (installAction != null) { - await activeAccount.saveInformation(StorageKey.InstalledVersion, currentVersion); + await stateService.setInstalledVersion(currentVersion); } }; } @@ -205,8 +195,7 @@ export function initFactory(): Function { UnauthGuardService, LockGuardService, ModalService, - { provide: AccountsManagementServiceAbstraction, useValue: accountsManagementService }, - { provide: ActiveAccountServiceAbstraction, useValue: activeAccount }, + SearchBarService, { provide: ApiServiceAbstraction, useValue: apiService }, { provide: AuditServiceAbstraction, useValue: auditService }, { provide: AuthServiceAbstraction, useValue: authService }, diff --git a/src/app/vault/add-edit.component.ts b/src/app/vault/add-edit.component.ts index b55e3448..491bd28f 100644 --- a/src/app/vault/add-edit.component.ts +++ b/src/app/vault/add-edit.component.ts @@ -7,7 +7,6 @@ import { } from '@angular/core'; import { NgForm } from '@angular/forms'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { AuditService } from 'jslib-common/abstractions/audit.service'; import { CipherService } from 'jslib-common/abstractions/cipher.service'; import { CollectionService } from 'jslib-common/abstractions/collection.service'; @@ -40,11 +39,9 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges, collectionService: CollectionService, messagingService: MessagingService, eventService: EventService, policyService: PolicyService, private broadcasterService: BroadcasterService, private ngZone: NgZone, - logService: LogService, activeAccountService: ActiveAccountService, - organizationService: OrganizationService) { + logService: LogService, organizationService: OrganizationService) { super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService, - collectionService, messagingService, eventService, policyService, logService, activeAccountService, - organizationService); + collectionService, messagingService, eventService, policyService, logService, organizationService); } async ngOnInit() { diff --git a/src/app/vault/attachments.component.ts b/src/app/vault/attachments.component.ts index 6dca4e98..dafe18a5 100644 --- a/src/app/vault/attachments.component.ts +++ b/src/app/vault/attachments.component.ts @@ -1,12 +1,12 @@ import { Component } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { CipherService } from 'jslib-common/abstractions/cipher.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { AttachmentsComponent as BaseAttachmentsComponent } from 'jslib-angular/components/attachments.component'; @@ -18,8 +18,8 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { constructor(cipherService: CipherService, i18nService: I18nService, cryptoService: CryptoService, platformUtilsService: PlatformUtilsService, apiService: ApiService, logService: LogService, - activeAccountService: ActiveAccountService) { + stateService: StateService) { super(cipherService, i18nService, cryptoService, platformUtilsService, - apiService, window, logService, activeAccountService); + apiService, window, logService, stateService); } } diff --git a/src/app/vault/ciphers.component.html b/src/app/vault/ciphers.component.html index c9210d1a..c6e785ce 100644 --- a/src/app/vault/ciphers.component.html +++ b/src/app/vault/ciphers.component.html @@ -1,10 +1,3 @@ -
diff --git a/src/app/vault/ciphers.component.ts b/src/app/vault/ciphers.component.ts index c9c1cff4..ee9da403 100644 --- a/src/app/vault/ciphers.component.ts +++ b/src/app/vault/ciphers.component.ts @@ -1,14 +1,27 @@ import { Component } from '@angular/core'; import { CiphersComponent as BaseCiphersComponent } from 'jslib-angular/components/ciphers.component'; +import { SearchService } from 'jslib-common/abstractions/search.service'; import { CipherView } from 'jslib-common/models/view/cipherView'; +import { SearchBarService } from '../layout/search/search-bar.service'; + @Component({ selector: 'app-vault-ciphers', templateUrl: 'ciphers.component.html', }) export class CiphersComponent extends BaseCiphersComponent { + + constructor(searchService: SearchService, searchBarService: SearchBarService) { + super(searchService); + + searchBarService.searchText.subscribe(searchText => { + this.searchText = searchText; + this.search(200); + }); + } + trackByFn(index: number, c: CipherView) { return c.id; } diff --git a/src/app/vault/groupings.component.ts b/src/app/vault/groupings.component.ts index 34716b98..3f6d5e0a 100644 --- a/src/app/vault/groupings.component.ts +++ b/src/app/vault/groupings.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { CollectionService } from 'jslib-common/abstractions/collection.service'; import { FolderService } from 'jslib-common/abstractions/folder.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { GroupingsComponent as BaseGroupingsComponent } from 'jslib-angular/components/groupings.component'; @@ -12,7 +12,7 @@ import { GroupingsComponent as BaseGroupingsComponent } from 'jslib-angular/comp }) export class GroupingsComponent extends BaseGroupingsComponent { constructor(collectionService: CollectionService, folderService: FolderService, - activeAccountService: ActiveAccountService) { - super(collectionService, folderService, activeAccountService); + stateService: StateService) { + super(collectionService, folderService, stateService); } } diff --git a/src/app/vault/share.component.ts b/src/app/vault/share.component.ts index b5ff612e..afb0a045 100644 --- a/src/app/vault/share.component.ts +++ b/src/app/vault/share.component.ts @@ -1,6 +1,5 @@ import { Component } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { CipherService } from 'jslib-common/abstractions/cipher.service'; import { CollectionService } from 'jslib-common/abstractions/collection.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; @@ -17,9 +16,8 @@ import { ShareComponent as BaseShareComponent } from 'jslib-angular/components/s export class ShareComponent extends BaseShareComponent { constructor(cipherService: CipherService, i18nService: I18nService, collectionService: CollectionService, platformUtilsService: PlatformUtilsService, - logService: LogService, activeAccountService: ActiveAccountService, - organizationService: OrganizationService) { + logService: LogService, organizationService: OrganizationService) { super(collectionService, platformUtilsService, i18nService, cipherService, - logService, activeAccountService, organizationService); + logService, organizationService); } } diff --git a/src/app/vault/vault.component.ts b/src/app/vault/vault.component.ts index aac7ff37..f3cec392 100644 --- a/src/app/vault/vault.component.ts +++ b/src/app/vault/vault.component.ts @@ -16,8 +16,7 @@ import { first } from 'rxjs/operators'; import { ToasterService } from 'angular2-toaster'; -import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; - +import { SearchBarService } from '../layout/search/search-bar.service'; import { AddEditComponent } from './add-edit.component'; import { AttachmentsComponent } from './attachments.component'; import { CiphersComponent } from './ciphers.component'; @@ -38,16 +37,18 @@ import { FolderView } from 'jslib-common/models/view/folderView'; import { ModalRef } from 'jslib-angular/components/modal/modal.ref'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; +import { BroadcasterService } from 'jslib-angular/services/broadcaster.service'; +import { ModalService } from 'jslib-angular/services/modal.service'; + import { EventService } from 'jslib-common/abstractions/event.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; import { TotpService } from 'jslib-common/abstractions/totp.service'; -import { ModalService } from 'jslib-angular/services/modal.service'; import { invokeMenu, RendererMenuItem } from 'jslib-electron/utils'; @@ -91,10 +92,10 @@ export class VaultComponent implements OnInit, OnDestroy { private toasterService: ToasterService, private messagingService: MessagingService, private platformUtilsService: PlatformUtilsService, private eventService: EventService, private totpService: TotpService, private passwordRepromptService: PasswordRepromptService, - private activeAccount: ActiveAccountService) { } + private stateService: StateService, private searchBarService: SearchBarService) { } async ngOnInit() { - this.userHasPremiumAccess = this.activeAccount.canAccessPremium; + this.userHasPremiumAccess = await this.stateService.getCanAccessPremium(); this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { this.ngZone.run(async () => { let detectChanges = true; @@ -170,9 +171,13 @@ export class VaultComponent implements OnInit, OnDestroy { await this.load(); } document.body.classList.remove('layout_frontend'); + + this.searchBarService.setEnabled(true); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchVault')); } ngOnDestroy() { + this.searchBarService.setEnabled(false); this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); document.body.classList.add('layout_frontend'); } @@ -498,14 +503,14 @@ export class VaultComponent implements OnInit, OnDestroy { } async clearGroupingFilters() { - this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchVault'); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchVault')); await this.ciphersComponent.reload(); this.clearFilters(); this.go(); } async filterFavorites() { - this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchFavorites'); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchFavorites')); await this.ciphersComponent.reload(c => c.favorite); this.clearFilters(); this.favorites = true; @@ -513,7 +518,7 @@ export class VaultComponent implements OnInit, OnDestroy { } async filterDeleted() { - this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchTrash'); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchTrash')); this.ciphersComponent.deleted = true; await this.ciphersComponent.reload(null, true); this.clearFilters(); @@ -522,7 +527,7 @@ export class VaultComponent implements OnInit, OnDestroy { } async filterCipherType(type: CipherType) { - this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchType'); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchType')); await this.ciphersComponent.reload(c => c.type === type); this.clearFilters(); this.type = type; @@ -531,7 +536,7 @@ export class VaultComponent implements OnInit, OnDestroy { async filterFolder(folderId: string) { folderId = folderId === 'none' ? null : folderId; - this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchFolder'); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchFolder')); await this.ciphersComponent.reload(c => c.folderId === folderId); this.clearFilters(); this.folderId = folderId == null ? 'none' : folderId; @@ -539,7 +544,7 @@ export class VaultComponent implements OnInit, OnDestroy { } async filterCollection(collectionId: string) { - this.ciphersComponent.searchPlaceholder = this.i18nService.t('searchCollection'); + this.searchBarService.setPlaceholderText(this.i18nService.t('searchCollection')); await this.ciphersComponent.reload(c => c.collectionIds != null && c.collectionIds.indexOf(collectionId) > -1); this.clearFilters(); diff --git a/src/app/vault/view.component.ts b/src/app/vault/view.component.ts index fc4f78d6..b15d8eda 100644 --- a/src/app/vault/view.component.ts +++ b/src/app/vault/view.component.ts @@ -7,7 +7,6 @@ import { Output, } from '@angular/core'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { AuditService } from 'jslib-common/abstractions/audit.service'; import { CipherService } from 'jslib-common/abstractions/cipher.service'; @@ -18,6 +17,7 @@ import { LogService } from 'jslib-common/abstractions/log.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { TokenService } from 'jslib-common/abstractions/token.service'; import { TotpService } from 'jslib-common/abstractions/totp.service'; @@ -43,10 +43,10 @@ export class ViewComponent extends BaseViewComponent implements OnChanges { ngZone: NgZone, changeDetectorRef: ChangeDetectorRef, eventService: EventService, apiService: ApiService, private messagingService: MessagingService, passwordRepromptService: PasswordRepromptService, - logService: LogService, activeAccountService: ActiveAccountService) { + logService: LogService, stateService: StateService) { super(cipherService, totpService, tokenService, i18nService, cryptoService, platformUtilsService, auditService, window, broadcasterService, ngZone, changeDetectorRef, eventService, - apiService, passwordRepromptService, logService, activeAccountService); + apiService, passwordRepromptService, logService, stateService); } ngOnInit() { super.ngOnInit(); diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 1610f8ef..9ed8b57c 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -1736,5 +1736,8 @@ }, "personalVaultExportPolicyInEffect": { "message": "One or more organization policies prevents you from exporting your personal vault." + }, + "addAccount": { + "message": "Add Account" } } diff --git a/src/main.ts b/src/main.ts index 16100979..053f1f3f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,20 +20,14 @@ import { UpdaterMain } from 'jslib-electron/updater.main'; import { WindowMain } from 'jslib-electron/window.main'; import { NativeMessagingMain } from './main/nativeMessaging.main'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - -import { AccountsManagementService } from 'jslib-common/services/accountsManagement.service'; -import { ActiveAccountService } from 'jslib-common/services/activeAccount.service'; -import { StoreService } from 'jslib-common/services/store.service'; +import { StateService } from 'jslib-common/services/state.service'; export class Main { logService: ElectronLogService; i18nService: I18nService; storageService: ElectronStorageService; messagingService: ElectronMainMessagingService; - accountsManagementService: AccountsManagementService; - activeAccount: ActiveAccountService; - storeService: StoreService; + stateService: StateService; keytarStorageListener: KeytarStorageListener; windowMain: WindowMain; @@ -78,16 +72,14 @@ export class Main { const storageDefaults: any = {}; // Default vault timeout to "on restart", and action to "lock" - storageDefaults[StorageKey.VaultTimeout] = -1; - storageDefaults[StorageKey.VaultTimeoutAction] = 'lock'; + storageDefaults['global.vaultTimeout'] = -1; + storageDefaults['global.vaultTimeoutAction'] = 'lock'; this.storageService = new ElectronStorageService(app.getPath('userData'), storageDefaults); - this.accountsManagementService = new AccountsManagementService(this.storageService, null); - this.storeService = new StoreService(this.storageService, null); - this.activeAccount = new ActiveAccountService(this.accountsManagementService, this.storeService); + this.stateService = new StateService(this.storageService, null, this.logService); - this.windowMain = new WindowMain(this.storageService, this.logService, true, undefined, undefined, + this.windowMain = new WindowMain(this.stateService, this.logService, true, undefined, undefined, arg => this.processDeepLink(arg), win => this.trayMain.setupWindowListeners(win)); - this.messagingMain = new MessagingMain(this, this.storageService); + this.messagingMain = new MessagingMain(this, this.stateService); this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, 'desktop', () => { this.menuMain.updateMenuItem.enabled = false; }, () => { @@ -97,7 +89,7 @@ export class Main { }, 'bitwarden'); this.menuMain = new MenuMain(this); this.powerMonitorMain = new PowerMonitorMain(this); - this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.storageService); + this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.stateService); this.messagingService = new ElectronMainMessagingService(this.windowMain, message => { this.messagingMain.onMessage(message); @@ -109,7 +101,7 @@ export class Main { this.biometricMain = new BiometricWindowsMain(this.i18nService, this.windowMain, this.storageService); } else if (process.platform === 'darwin') { const BiometricDarwinMain = require('jslib-electron/biometric.darwin.main').default; - this.biometricMain = new BiometricDarwinMain(this.i18nService, this.storageService); + this.biometricMain = new BiometricDarwinMain(this.i18nService, this.stateService); } this.keytarStorageListener = new KeytarStorageListener('Bitwarden', this.biometricMain); @@ -120,7 +112,7 @@ export class Main { bootstrap() { this.keytarStorageListener.init(); this.windowMain.init().then(async () => { - const locale = await this.activeAccount.getInformation(StorageKey.Locale); + const locale = await this.stateService.getLocale(); await this.i18nService.init(locale != null ? locale : app.getLocale()); this.messagingMain.init(); this.menuMain.init(); @@ -130,7 +122,7 @@ export class Main { id: 'lockNow', click: () => this.messagingService.send('lockVault'), }]); - if (await this.activeAccount.getInformation(StorageKey.EnableStartToTrayKey)) { + if (await this.stateService.getEnableStartToTray()) { this.trayMain.hideToTray(); } this.powerMonitorMain.init(); @@ -139,7 +131,7 @@ export class Main { await this.biometricMain.init(); } - if (await this.activeAccount.getInformation(StorageKey.EnableBrowserIntegration)) { + if (await this.stateService.getEnableBrowserIntegration()) { this.nativeMessagingMain.listen(); } diff --git a/src/main/menu.main.ts b/src/main/menu.main.ts index 2084cadc..10d4ee1f 100644 --- a/src/main/menu.main.ts +++ b/src/main/menu.main.ts @@ -10,8 +10,6 @@ import { import { Main } from '../main'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - import { BaseMenu } from 'jslib-electron/baseMenu'; import { isMacAppStore, isSnapStore, isWindowsStore } from 'jslib-electron/utils'; @@ -504,7 +502,7 @@ export class MenuMain extends BaseMenu { private async openWebVault() { let webUrl = 'https://vault.bitwarden.com'; - const urlsObj: any = await this.main.activeAccount.getInformation(StorageKey.EnvironmentUrls); + const urlsObj: any = await this.main.stateService.getEnvironmentUrls(); if (urlsObj != null) { if (urlsObj.base != null) { webUrl = urlsObj.base; diff --git a/src/main/messaging.main.ts b/src/main/messaging.main.ts index 6020bdd9..817d6d7e 100644 --- a/src/main/messaging.main.ts +++ b/src/main/messaging.main.ts @@ -4,24 +4,22 @@ import * as path from 'path'; import { Main } from '../main'; -import { StorageService } from 'jslib-common/abstractions/storage.service'; - -import { StorageKey } from 'jslib-common/enums/storageKey'; +import { StateService } from 'jslib-common/abstractions/state.service'; const SyncInterval = 5 * 60 * 1000; // 5 minutes export class MessagingMain { private syncTimeout: NodeJS.Timer; - constructor(private main: Main, private storageService: StorageService) { } + constructor(private main: Main, private stateService: StateService) { } init() { this.scheduleNextSync(); if (process.platform === 'linux') { - this.storageService.save(StorageKey.OpenAtLogin, fs.existsSync(this.linuxStartupFile())); + this.stateService.setOpenAtLogin(fs.existsSync(this.linuxStartupFile())); } else { const loginSettings = app.getLoginItemSettings(); - this.storageService.save(StorageKey.OpenAtLogin, loginSettings.openAtLogin); + this.stateService.setOpenAtLogin(loginSettings.openAtLogin); } ipcMain.on('messagingService', async (event: any, message: any) => this.onMessage(message)); } @@ -36,7 +34,7 @@ export class MessagingMain { this.updateTrayMenu(message.isAuthenticated, message.isLocked); break; case 'minimizeOnCopy': - this.storageService.get(StorageKey.MinimizeOnCopyToClipboardKey).then( + this.stateService.getMinimizeOnCopyToClipboard().then( shouldMinimize => { if (shouldMinimize && this.main.windowMain.win !== null) { this.main.windowMain.win.minimize(); diff --git a/src/main/powerMonitor.main.ts b/src/main/powerMonitor.main.ts index 9c3d518c..78f79134 100644 --- a/src/main/powerMonitor.main.ts +++ b/src/main/powerMonitor.main.ts @@ -1,7 +1,5 @@ import { powerMonitor } from 'electron'; -import { StorageKey } from 'jslib-common/enums/storageKey'; - import { isSnapStore } from 'jslib-electron/utils'; import { Main } from '../main'; @@ -60,8 +58,8 @@ export class PowerMonitorMain { } private async getVaultTimeoutOptions(): Promise<[number, string]> { - const timeout = await this.main.activeAccount.getInformation(StorageKey.VaultTimeout); - const action = await this.main.activeAccount.getInformation(StorageKey.VaultTimeoutAction); + const timeout = await this.main.stateService.getVaultTimeout(); + const action = await this.main.stateService.getVaultTimeoutAction(); return [timeout, action]; } } diff --git a/src/scss/header.scss b/src/scss/header.scss new file mode 100644 index 00000000..97086594 --- /dev/null +++ b/src/scss/header.scss @@ -0,0 +1,175 @@ +.header { + -webkit-app-region: drag; + min-height: 44px; + max-height: 44px; + border-bottom: 1px solid #000000; + display: grid; + grid-template-columns: 25% 1fr 25%; + grid-column-gap: 5px; + justify-items: center; + align-items: center; + + @include themify($themes) { + background-color: themed('headerBackgroundColor'); + border-bottom-color: themed('headerBorderColor'); + } + + app-search { + grid-column-start: 2; + width: 100%; + } + + app-account-switcher { + justify-self: end; + height: 100%; + } + + .search { + padding: 0 7px; + width: 100%; + text-align: left; + position: relative; + + .fa { + position: absolute; + top: 7px; + left: 15px; + + @include themify($themes) { + color: themed('headerInputPlaceholderColor'); + } + } + + input { + width: 100%; + margin: 0; + border: none; + padding: 5px 10px 5px 30px; + border-radius: $border-radius; + + @include themify($themes) { + background-color: themed('headerInputBackgroundColor'); + color: themed('headerInputColor'); + } + + &:focus { + border-radius: $border-radius; + outline: none; + + @include themify($themes) { + background-color: themed('headerInputBackgroundFocusColor'); + } + } + + &::-webkit-input-placeholder { + @include themify($themes) { + color: themed('headerInputPlaceholderColor'); + } + } + } + } +} + +.account-switcher { + display: grid; + grid-template-columns: auto 1fr auto; + grid-column-gap: 5px; + align-items: center; + justify-items: center; + padding: 0 10px; + height: 100%; + user-select: none; + + @include themify($themes) { + color: themed('headerInputColor'); // TODO: Change to headerTextColor? + } + + img { + display: block; + } + + span { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &:hover { + @include themify($themes) { + background-color: themed('headerBorderColor'); + color: themed('headerInputColor'); // TODO: Change to headerTextColor? + } + } +} + +.account-switcher-dropdown { + @include themify($themes) { + background-color: themed('headerBorderColor'); + } + margin-right: 5px; + margin-top: 1px; // Fix for border-bottom in header + width: 100%; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2); + border-radius: $border-radius; + + a { + padding: 5px 10px; + display: block; + + @include themify($themes) { + color: themed('textColor'); + } + + &:hover { + @include themify($themes) { + background-color: themed('backgroundColorAlt2'); // TODO: Check if this color is acceptable design prototype uses #ececec + } + } + } + + .accounts { + padding: 4px 0; + + .account { + display: grid; + grid-column-gap: 5px; + grid-template: + [row1-start] "email status" [row1-end] + [row2-start] "server server" [row2-end] + / 1fr auto; + align-items: baseline; + + .server { + font-size: $font-size-small; + } + + .email { + font-size: $font-size-large + } + + .status { + font-style: italic; + grid-area: status; + } + + &.active { + font-weight: 600; + } + } + } + + .border { + @include themify($themes) { + background: themed('borderColor'); + } + left: 10px; + width: calc(100% - 20px); + height: 1px; + position: relative; + } + + .add { + margin: 4px 0; + } +} diff --git a/src/scss/misc.scss b/src/scss/misc.scss index d9699e81..0a8c0dab 100644 --- a/src/scss/misc.scss +++ b/src/scss/misc.scss @@ -431,3 +431,7 @@ app-root > #loading { margin-bottom: 15px; } } + +.rounded-circle { + border-radius:50% !important; +} diff --git a/src/scss/pages.scss b/src/scss/pages.scss index 109a7f80..1630ee32 100644 --- a/src/scss/pages.scss +++ b/src/scss/pages.scss @@ -124,6 +124,27 @@ } } +.login-header { + padding: 1em; + font-size: 1.2em; + .environment-urls-settings-icon { + @include themify($themes) { + color: themed('mutedColor'); + } + + span { + visibility: hidden; + } + + &:hover, &:focus { + text-decoration: none; + + @include themify($themes) { + color: themed('primaryColor'); + } + } + } +} #sso-page { .content { width: 300px; diff --git a/src/scss/styles.scss b/src/scss/styles.scss index ec5ac20d..788485c6 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -1,4 +1,5 @@ -@import "../../jslib/angular/src/scss/webfonts.css"; +@import '~@angular/cdk/overlay-prebuilt.css'; +@import "../css/webfonts.css"; @import "variables.scss"; @import "base.scss"; @import "grid.scss"; @@ -11,3 +12,4 @@ @import "modal.scss"; @import "plugins.scss"; @import "environment.scss"; +@import "header.scss"; diff --git a/src/scss/vault.scss b/src/scss/vault.scss index ccbe7441..c9f8f539 100644 --- a/src/scss/vault.scss +++ b/src/scss/vault.scss @@ -1,5 +1,16 @@ @import "variables.scss"; +app-root { + display: flex; + flex-flow: column; + height: 100%; +} + +#container { + height: 100%; + min-height: 0; +} + .vault { height: 100%; display: flex; @@ -356,67 +367,6 @@ } } - .header { - min-height: 44px; - max-height: 44px; - flex: 0 0 auto; - border-bottom: 1px solid #000000; - display: flex; - align-items: center; - - @include themify($themes) { - background-color: themed('headerBackgroundColor'); - border-bottom-color: themed('headerBorderColor'); - } - - &.header-search { - .search { - padding: 0 7px; - width: 100%; - text-align: left; - position: relative; - - .fa { - position: absolute; - top: 7px; - left: 15px; - - @include themify($themes) { - color: themed('headerInputPlaceholderColor'); - } - } - - input { - width: 100%; - margin: 0; - border: none; - padding: 5px 10px 5px 30px; - border-radius: $border-radius; - - @include themify($themes) { - background-color: themed('headerInputBackgroundColor'); - color: themed('headerInputColor'); - } - - &:focus { - border-radius: $border-radius; - outline: none; - - @include themify($themes) { - background-color: themed('headerInputBackgroundFocusColor'); - } - } - - &::-webkit-input-placeholder { - @include themify($themes) { - color: themed('headerInputPlaceholderColor'); - } - } - } - } - } - } - .content { flex: 1 1 auto; position: relative; diff --git a/src/services/nativeMessaging.service.ts b/src/services/nativeMessaging.service.ts index c1ba0e89..18efe2c5 100644 --- a/src/services/nativeMessaging.service.ts +++ b/src/services/nativeMessaging.service.ts @@ -1,20 +1,20 @@ import { ipcRenderer } from 'electron'; import Swal from 'sweetalert2'; -import { ActiveAccountService } from 'jslib-common/abstractions/activeAccount.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { LogService } from 'jslib-common/abstractions/log.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service'; import { Utils } from 'jslib-common/misc/utils'; import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey'; -import { StorageKey } from 'jslib-common/enums/storageKey'; +import { KeySuffixOptions } from 'jslib-common/enums/keySuffixOptions'; const MessageValidTimeout = 10 * 1000; const EncryptionAlgorithm = 'sha1'; @@ -25,7 +25,7 @@ export class NativeMessagingService { constructor(private cryptoFunctionService: CryptoFunctionService, private cryptoService: CryptoService, private platformUtilService: PlatformUtilsService, private logService: LogService, private i18nService: I18nService, private messagingService: MessagingService, - private vaultTimeoutService: VaultTimeoutService, private activeAccount: ActiveAccountService) { + private vaultTimeoutService: VaultTimeoutService, private stateService: StateService) { ipcRenderer.on('nativeMessaging', async (_event: any, message: any) => { this.messageHandler(message); }); @@ -40,15 +40,15 @@ export class NativeMessagingService { const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey).buffer; // Valudate the UserId to ensure we are logged into the same account. - if (rawMessage.userId !== this.activeAccount.userId) { + if (rawMessage.userId !== await this.stateService.getUserId()) { ipcRenderer.send('nativeMessagingReply', {command: 'wrongUserId', appId: appId}); return; } - if (await this.activeAccount.getInformation(StorageKey.EnableBrowserIntegrationFingerprint)) { + if (await this.stateService.getEnableBrowserIntegrationFingerprint()) { ipcRenderer.send('nativeMessagingReply', {command: 'verifyFingerprint', appId: appId}); - const fingerprint = (await this.cryptoService.getFingerprint(this.activeAccount.userId, remotePublicKey)).join(' '); + const fingerprint = (await this.cryptoService.getFingerprint(await this.stateService.getUserId(), remotePublicKey)).join(' '); this.messagingService.send('setFocus'); @@ -103,7 +103,7 @@ export class NativeMessagingService { }); } - const keyB64 = (await this.cryptoService.getKeyFromStorage('biometric')).keyB64; + const keyB64 = (await this.cryptoService.getKeyFromStorage(KeySuffixOptions.Biometric)).keyB64; if (keyB64 != null) { this.send({ command: 'biometricUnlock', response: 'unlocked', keyB64: keyB64 }, appId);