diff --git a/jslib b/jslib index 4c083eeb92d..fe3a8785422 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 4c083eeb92d599bd8b85fb88ec5d435079b79395 +Subproject commit fe3a8785422a8866dbcb91d31db6d3fd2aa2daaa diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 80970051b72..b7f8923f841 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,6 +4,10 @@ import { Routes, } from '@angular/router'; +import { FrontendLayoutComponent } from './layouts/frontend-layout.component'; +import { OrganizationLayoutComponent } from './layouts/organization-layout.component'; +import { UserLayoutComponent } from './layouts/user-layout.component'; + import { HintComponent } from './accounts/hint.component'; import { LoginComponent } from './accounts/login.component'; import { RegisterComponent } from './accounts/register.component'; @@ -12,15 +16,32 @@ import { TwoFactorComponent } from './accounts/two-factor.component'; import { VaultComponent } from './vault/vault.component'; const routes: Routes = [ - { path: '', redirectTo: '/vault', pathMatch: 'full' }, - { path: 'login', component: LoginComponent }, - { path: '2fa', component: TwoFactorComponent }, - { path: 'register', component: RegisterComponent }, - { path: 'hint', component: HintComponent }, { - path: 'vault', - component: VaultComponent, + path: '', + component: UserLayoutComponent, + children: [ + { path: '', redirectTo: 'vault', pathMatch: 'full' }, + { path: 'vault', component: VaultComponent }, + ], }, + { + path: '', + component: FrontendLayoutComponent, + children: [ + { path: 'login', component: LoginComponent }, + { path: '2fa', component: TwoFactorComponent }, + { path: 'register', component: RegisterComponent }, + { path: 'hint', component: HintComponent }, + ], + }, + { + path: 'organization/:organizationId', + component: OrganizationLayoutComponent, + children: [ + { path: 'vault', component: VaultComponent }, + ], + }, + { path: '**', redirectTo: '' }, ]; @NgModule({ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 66bdaca1b00..b806a07ef8e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,19 +1,45 @@ import { ToasterConfig, ToasterContainerComponent, + ToasterService, } from 'angular2-toaster'; -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; +import { Angulartics2 } from 'angulartics2'; import { Component, + NgZone, + OnDestroy, + OnInit, } from '@angular/core'; import { Router } from '@angular/router'; +import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; + +import { StorageService } from 'jslib/abstractions/storage.service'; + +import { AuthService } from 'jslib/abstractions/auth.service'; +import { CipherService } from 'jslib/abstractions/cipher.service'; +import { CollectionService } from 'jslib/abstractions/collection.service'; +import { CryptoService } from 'jslib/abstractions/crypto.service'; +import { FolderService } from 'jslib/abstractions/folder.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { LockService } from 'jslib/abstractions/lock.service'; +import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { SettingsService } from 'jslib/abstractions/settings.service'; +import { SyncService } from 'jslib/abstractions/sync.service'; +import { TokenService } from 'jslib/abstractions/token.service'; +import { UserService } from 'jslib/abstractions/user.service'; + +import { ConstantsService } from 'jslib/services/constants.service'; + +const BroadcasterSubscriptionId = 'AppComponent'; + @Component({ selector: 'app-root', templateUrl: 'app.component.html', }) -export class AppComponent { +export class AppComponent implements OnDestroy, OnInit { toasterConfig: ToasterConfig = new ToasterConfig({ showCloseButton: true, mouseoverTimerStop: true, @@ -22,7 +48,90 @@ export class AppComponent { positionClass: 'toast-bottom-right', }); - constructor() { - // + private lastActivity: number = null; + + 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, + private authService: AuthService, private router: Router, private analytics: Angulartics2, + private toasterService: ToasterService, private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, private ngZone: NgZone, + private lockService: LockService, private storageService: StorageService, + private cryptoService: CryptoService, private collectionService: CollectionService) { } + + ngOnInit() { + this.ngZone.runOutsideAngular(() => { + window.onmousemove = () => this.recordActivity(); + window.onmousedown = () => this.recordActivity(); + window.ontouchstart = () => this.recordActivity(); + window.onclick = () => this.recordActivity(); + window.onscroll = () => this.recordActivity(); + window.onkeypress = () => this.recordActivity(); + }); + + this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { + this.ngZone.run(async () => { + switch (message.command) { + case 'loggedIn': + case 'unlocked': + case 'loggedOut': + break; + case 'logout': + this.logOut(!!message.expired); + break; + case 'lockVault': + //await this.lockService.lock(); + break; + case 'locked': + //this.router.navigate(['lock']); + break; + case 'syncStarted': + break; + case 'syncCompleted': + break; + default: + } + }); + }); + } + + ngOnDestroy() { + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + } + + private async logOut(expired: boolean) { + const userId = await this.userService.getUserId(); + + await Promise.all([ + this.syncService.setLastSync(new Date(0)), + this.tokenService.clearToken(), + this.cryptoService.clearKeys(), + this.userService.clear(), + this.settingsService.clear(userId), + this.cipherService.clear(userId), + this.folderService.clear(userId), + this.collectionService.clear(userId), + this.passwordGenerationService.clear(), + ]); + + this.authService.logOut(async () => { + this.analytics.eventTrack.next({ action: 'Logged Out' }); + if (expired) { + this.toasterService.popAsync('warning', this.i18nService.t('loggedOut'), + this.i18nService.t('loginExpired')); + } + this.router.navigate(['login']); + }); + } + + private async recordActivity() { + const now = (new Date()).getTime(); + if (this.lastActivity != null && now - this.lastActivity < 250) { + return; + } + + this.lastActivity = now; + this.storageService.save(ConstantsService.lastActiveKey, now); } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 61d24feac53..c2932a377a8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,6 +16,12 @@ import { ServicesModule } from './services.module'; import { AppComponent } from './app.component'; import { ModalComponent } from './modal.component'; +import { FooterComponent } from './layouts/footer.component'; +import { FrontendLayoutComponent } from './layouts/frontend-layout.component'; +import { NavbarComponent } from './layouts/navbar.component'; +import { OrganizationLayoutComponent } from './layouts/organization-layout.component'; +import { UserLayoutComponent } from './layouts/user-layout.component'; + import { HintComponent } from './accounts/hint.component'; import { LoginComponent } from './accounts/login.component'; import { RegisterComponent } from './accounts/register.component'; @@ -69,6 +75,8 @@ import { Folder } from 'jslib/models/domain'; CiphersComponent, FallbackSrcDirective, FolderAddEditComponent, + FooterComponent, + FrontendLayoutComponent, GroupingsComponent, HintComponent, IconComponent, @@ -76,12 +84,15 @@ import { Folder } from 'jslib/models/domain'; InputVerbatimDirective, LoginComponent, ModalComponent, + NavbarComponent, + OrganizationLayoutComponent, RegisterComponent, SearchCiphersPipe, StopClickDirective, StopPropDirective, TwoFactorComponent, TwoFactorOptionsComponent, + UserLayoutComponent, VaultComponent, ], entryComponents: [ diff --git a/src/app/layouts/footer.component.html b/src/app/layouts/footer.component.html new file mode 100644 index 00000000000..116376c426f --- /dev/null +++ b/src/app/layouts/footer.component.html @@ -0,0 +1,13 @@ +
diff --git a/src/app/layouts/footer.component.ts b/src/app/layouts/footer.component.ts new file mode 100644 index 00000000000..225beb1a4d0 --- /dev/null +++ b/src/app/layouts/footer.component.ts @@ -0,0 +1,9 @@ +import { + Component, +} from '@angular/core'; + +@Component({ + selector: 'app-footer', + templateUrl: 'footer.component.html', +}) +export class FooterComponent { } diff --git a/src/app/layouts/frontend-layout.component.html b/src/app/layouts/frontend-layout.component.html new file mode 100644 index 00000000000..4cca73f7a24 --- /dev/null +++ b/src/app/layouts/frontend-layout.component.html @@ -0,0 +1,2 @@ +