From 0fecb8a4bae88ca1c3533bf6789e224b4eedfe32 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Mon, 15 Feb 2021 11:45:04 -0600 Subject: [PATCH] #84 - Add login page as part of SPA instead of relying on external page --- src/app/app-routing.module.ts | 14 +- src/app/app.component.ts | 101 +++++----- src/app/components/components.module.ts | 4 + .../renderers/boolean-renderer.component.ts | 1 - .../option-picker/option-picker.component.ts | 6 +- src/app/home/home.page.ts | 1 - src/app/list/list.module.ts | 23 --- src/app/list/list.page.html | 27 --- src/app/list/list.page.scss | 1 - src/app/list/list.page.spec.ts | 32 ---- src/app/list/list.page.ts | 39 ---- src/app/pages/login/login.page.html | 94 ++++++++++ src/app/pages/login/login.page.scss | 5 + src/app/pages/login/login.page.ts | 175 ++++++++++++++++++ src/app/service/api.service.ts | 62 +++++++ src/app/service/auth.service.ts | 59 ++++++ src/app/service/guard/GuestOnly.guard.ts | 41 ++++ src/app/service/navigation.service.ts | 7 + 18 files changed, 519 insertions(+), 173 deletions(-) delete mode 100644 src/app/list/list.module.ts delete mode 100644 src/app/list/list.page.html delete mode 100644 src/app/list/list.page.scss delete mode 100644 src/app/list/list.page.spec.ts delete mode 100644 src/app/list/list.page.ts create mode 100644 src/app/pages/login/login.page.html create mode 100644 src/app/pages/login/login.page.scss create mode 100644 src/app/pages/login/login.page.ts create mode 100644 src/app/service/auth.service.ts create mode 100644 src/app/service/guard/GuestOnly.guard.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 68466ff..0cc5b2b 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,5 +1,8 @@ import { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; +import {LoginPage} from './pages/login/login.page'; +import {AuthService} from './service/auth.service'; +import {GuestOnlyGuard} from './service/guard/GuestOnly.guard'; const routes: Routes = [ { @@ -9,15 +12,18 @@ const routes: Routes = [ }, { path: 'home', + canActivate: [AuthService], loadChildren: () => import('./home/home.module').then(m => m.HomePageModule) }, - { - path: 'list', - loadChildren: () => import('./list/list.module').then(m => m.ListPageModule) - }, { path: 'editor', + canActivate: [AuthService], loadChildren: () => import('./components/components.module').then( m => m.ComponentsModule) + }, + { + path: 'login', + canActivate: [GuestOnlyGuard], + component: LoginPage, } ]; diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9b5993e..1aad1e8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -24,6 +24,7 @@ import {NavigationService} from './service/navigation.service'; import {DatabaseService} from './service/db/database.service'; import {EditorService} from './service/editor.service'; import {debug} from './utility'; +import {AuthService} from './service/auth.service'; @Component({ selector: 'app-root', @@ -96,7 +97,6 @@ export class AppComponent implements OnInit { protected showedNewVersionAlert = false; protected showedOfflineAlert = false; protected backbuttonSubscription: any; - protected initialized$: BehaviorSubject = new BehaviorSubject(false); constructor( private platform: Platform, @@ -114,6 +114,7 @@ export class AppComponent implements OnInit { protected toasts: ToastController, protected db: DatabaseService, protected editor: EditorService, + protected auth: AuthService, ) { } async checkNewVersion() { @@ -479,9 +480,11 @@ export class AppComponent implements OnInit { } async initializeApp() { + const initializedOnce = this.navService.initialized$.getValue(); + debug('app', this); this.loader = await this.loading.create({ - message: 'Starting up...', + message: 'Setting things up...', cssClass: 'noded-loading-mask', showBackdrop: true, }); @@ -493,37 +496,33 @@ export class AppComponent implements OnInit { let toast: any; - debug('Subscribing to offline changes...'); - this.api.offline$.subscribe(async isOffline => { - if ( isOffline && !this.showedOfflineAlert ) { - debug('Application went offline!'); - toast = await this.toasts.create({ - cssClass: 'compat-toast-container', - message: 'Uh, oh! It looks like you\'re offline. Some features might not work as expected...', - }); - - this.showedOfflineAlert = true; - await toast.present(); - } else if ( !isOffline && this.showedOfflineAlert ) { - debug('Appliation went online!'); - await toast.dismiss(); - this.showedOfflineAlert = false; - await this.api.syncOfflineData(); - } - }); + if ( !initializedOnce ) { + debug('Subscribing to offline changes...'); + this.api.offline$.subscribe(async isOffline => { + if ( isOffline && !this.showedOfflineAlert ) { + debug('Application went offline!'); + toast = await this.toasts.create({ + cssClass: 'compat-toast-container', + message: 'Uh, oh! It looks like you\'re offline. Some features might not work as expected...', + }); + + this.showedOfflineAlert = true; + await toast.present(); + } else if ( !isOffline && this.showedOfflineAlert ) { + debug('Appliation went online!'); + await toast.dismiss(); + this.showedOfflineAlert = false; + await this.api.syncOfflineData(); + } + }); + } debug('Getting initial status...'); let stat: any = await this.session.stat(); debug('Got stat:', stat); - if ( stat.public_user ) { - this.api.isPublicUser = true; - } - - if ( stat.authenticated_user ) { - this.api.isAuthenticated = true; - } - + this.api.isPublicUser = !!stat.public_user; + this.api.isAuthenticated = !!stat.authenticated_user; this.api.systemBase = stat.system_base; if ( !this.api.isAuthenticated || this.api.isPublicUser ) { @@ -558,7 +557,7 @@ export class AppComponent implements OnInit { debug('Initializing session...'); await this.session.initialize(); - if ( this.session.get('user.preferences.dark_mode') ) { + if ( this.session.get('user.preferences.dark_mode') && !this.darkMode ) { this.toggleDark(); } @@ -603,7 +602,7 @@ export class AppComponent implements OnInit { } } - this.initialized$.next(true); + this.navService.initialized$.next(true); if ( !this.api.isPublicUser && this.session.get('user.preferences.default_page') ) { debug('Navigating to default page!'); @@ -611,24 +610,38 @@ export class AppComponent implements OnInit { const node = this.findNode(id); if ( node ) { this.navigateEditorToNode(node); + } else if ( this.auth.authInProgress ) { + await this.router.navigate(['/home']); } + } else if ( this.auth.authInProgress ) { + await this.router.navigate(['/home']); } - debug('Creating menu subscription...'); - this.navService.sidebarRefresh$.subscribe(([_, quiet]) => { - this.onMenuRefresh(quiet); - }); + if ( !initializedOnce ) { + debug('Creating menu subscription...'); + this.navService.sidebarRefresh$.subscribe(([_, quiet]) => { + this.onMenuRefresh(quiet); + }); - this.navService.navigationRequest$.subscribe(pageId => { - debug('Page navigation request: ', {pageId}); - if ( !pageId ) { - debug('Empty page ID. Will not navigate.'); - return; - } + this.navService.navigationRequest$.subscribe(pageId => { + debug('Page navigation request: ', {pageId}); + if ( !pageId ) { + debug('Empty page ID. Will not navigate.'); + return; + } - this.currentPageId = pageId; - this.router.navigate(['/editor', { id: pageId }]); - }); + this.currentPageId = pageId; + this.router.navigate(['/editor', { id: pageId }]); + }); + + this.navService.initializationRequest$.subscribe((count) => { + if ( count === 0 ) { + return; + } + + this.initializeApp(); + }); + } debug('Reloading menu items...'); this.reloadMenuItems().subscribe(() => { @@ -647,6 +660,8 @@ export class AppComponent implements OnInit { }, 1000 * 60 * 5); // Check for new version every 5 mins } }); + + this.auth.authInProgress = false; } async doPrefetch() { diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index 30035c7..36babea 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -34,6 +34,7 @@ import {MarkdownModule} from 'ngx-markdown'; import {VersionModalComponent} from './version-modal/version-modal.component'; import {EditorPageRoutingModule} from '../pages/editor/editor-routing.module'; import {EditorPage} from '../pages/editor/editor.page'; +import {LoginPage} from '../pages/login/login.page'; import {WysiwygComponent} from './wysiwyg/wysiwyg.component'; import {WysiwygEditorComponent} from './editor/database/editors/wysiwyg/wysiwyg-editor.component'; import {WysiwygModalComponent} from './editor/database/editors/wysiwyg/wysiwyg-modal.component'; @@ -76,6 +77,7 @@ import {FileBoxPageComponent} from './nodes/file-box/file-box-page.component'; MarkdownEditorComponent, VersionModalComponent, EditorPage, + LoginPage, WysiwygComponent, WysiwygEditorComponent, WysiwygModalComponent, @@ -130,6 +132,7 @@ import {FileBoxPageComponent} from './nodes/file-box/file-box-page.component'; MarkdownEditorComponent, VersionModalComponent, EditorPage, + LoginPage, WysiwygComponent, WysiwygEditorComponent, WysiwygModalComponent, @@ -170,6 +173,7 @@ import {FileBoxPageComponent} from './nodes/file-box/file-box-page.component'; MarkdownEditorComponent, VersionModalComponent, EditorPage, + LoginPage, WysiwygComponent, WysiwygEditorComponent, WysiwygModalComponent, diff --git a/src/app/components/editor/database/renderers/boolean-renderer.component.ts b/src/app/components/editor/database/renderers/boolean-renderer.component.ts index 32fe248..bae0265 100644 --- a/src/app/components/editor/database/renderers/boolean-renderer.component.ts +++ b/src/app/components/editor/database/renderers/boolean-renderer.component.ts @@ -33,7 +33,6 @@ export class BooleanRendererComponent implements ICellRendererAngularComp { ) { } agInit(params: ICellRendererParams): void { - console.log('bool renderer', this); this.params = params; // @ts-ignore diff --git a/src/app/components/option-picker/option-picker.component.ts b/src/app/components/option-picker/option-picker.component.ts index b4338dd..fd94941 100644 --- a/src/app/components/option-picker/option-picker.component.ts +++ b/src/app/components/option-picker/option-picker.component.ts @@ -3,6 +3,7 @@ import {Router} from '@angular/router'; import {ApiService} from '../../service/api.service'; import {PopoverController} from '@ionic/angular'; import {DatabaseService} from '../../service/db/database.service'; +import {AuthService} from '../../service/auth.service'; @Component({ selector: 'app-option-picker', @@ -22,6 +23,7 @@ export class OptionPickerComponent implements OnInit { protected router: Router, protected popover: PopoverController, protected db: DatabaseService, + protected auth: AuthService, ) { } ngOnInit() {} @@ -30,8 +32,8 @@ export class OptionPickerComponent implements OnInit { if ( key === 'html_export' ) { window.open(this.api._build_url('/data/export/html'), '_blank'); } else if ( key === 'logout' ) { - await this.db.purge(); - window.location.href = '/auth/logout'; + await this.popover.dismiss(); + await this.auth.endSession(); } else if ( key === 'toggle_darkmode' ) { this.toggleDark(); } else if ( key === 'search_everywhere' ) { diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index ac1557d..ce564e3 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -8,7 +8,6 @@ import {isDebug, debug} from '../utility'; styleUrls: ['home.page.scss'], }) export class HomePage implements OnInit { - constructor( public readonly api: ApiService, ) {} diff --git a/src/app/list/list.module.ts b/src/app/list/list.module.ts deleted file mode 100644 index e8b3a79..0000000 --- a/src/app/list/list.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { IonicModule } from '@ionic/angular'; -import { RouterModule } from '@angular/router'; - -import { ListPage } from './list.page'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - IonicModule, - RouterModule.forChild([ - { - path: '', - component: ListPage - } - ]) - ], - declarations: [ListPage] -}) -export class ListPageModule {} diff --git a/src/app/list/list.page.html b/src/app/list/list.page.html deleted file mode 100644 index b166191..0000000 --- a/src/app/list/list.page.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - List - - - - - - - - - {{item.title}} -
- {{item.note}} -
-
-
- -
\ No newline at end of file diff --git a/src/app/list/list.page.scss b/src/app/list/list.page.scss deleted file mode 100644 index 8b13789..0000000 --- a/src/app/list/list.page.scss +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/list/list.page.spec.ts b/src/app/list/list.page.spec.ts deleted file mode 100644 index f27bc36..0000000 --- a/src/app/list/list.page.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { IonicModule } from '@ionic/angular'; - -import { ListPage } from './list.page'; - -describe('ListPage', () => { - let component: ListPage; - let fixture: ComponentFixture; - let listPage: HTMLElement; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ ListPage ], - imports: [IonicModule.forRoot()] - }).compileComponents(); - - fixture = TestBed.createComponent(ListPage); - component = fixture.componentInstance; - fixture.detectChanges(); - })); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should have a list of 10 elements', () => { - listPage = fixture.nativeElement; - const items = listPage.querySelectorAll('ion-item'); - expect(items.length).toEqual(10); - }); - -}); diff --git a/src/app/list/list.page.ts b/src/app/list/list.page.ts deleted file mode 100644 index 6cdaaa9..0000000 --- a/src/app/list/list.page.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-list', - templateUrl: 'list.page.html', - styleUrls: ['list.page.scss'] -}) -export class ListPage implements OnInit { - private selectedItem: any; - private icons = [ - 'flask', - 'wifi', - 'beer', - 'football', - 'basketball', - 'paper-plane', - 'american-football', - 'boat', - 'bluetooth', - 'build' - ]; - public items: Array<{ title: string; note: string; icon: string }> = []; - constructor() { - for (let i = 1; i < 11; i++) { - this.items.push({ - title: 'Item ' + i, - note: 'This is item #' + i, - icon: this.icons[Math.floor(Math.random() * this.icons.length)] - }); - } - } - - ngOnInit() { - } - // add back when alpha.4 is out - // navigate(item) { - // this.router.navigate(['/list', JSON.stringify(item)]); - // } -} diff --git a/src/app/pages/login/login.page.html b/src/app/pages/login/login.page.html new file mode 100644 index 0000000..8c2638b --- /dev/null +++ b/src/app/pages/login/login.page.html @@ -0,0 +1,94 @@ + \ No newline at end of file diff --git a/src/app/pages/login/login.page.scss b/src/app/pages/login/login.page.scss new file mode 100644 index 0000000..7a693e7 --- /dev/null +++ b/src/app/pages/login/login.page.scss @@ -0,0 +1,5 @@ +.login-container { + height: 100%; + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/src/app/pages/login/login.page.ts b/src/app/pages/login/login.page.ts new file mode 100644 index 0000000..720f40b --- /dev/null +++ b/src/app/pages/login/login.page.ts @@ -0,0 +1,175 @@ +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {AlertController, IonInput, LoadingController, ModalController, PopoverController} from '@ionic/angular'; +import {EditorService} from '../../service/editor.service'; +import {ApiService} from '../../service/api.service'; +import {NavigationService} from '../../service/navigation.service'; +import {AuthService} from '../../service/auth.service'; + +export type LoginPageStep = 'greeter' | 'username' | 'password' | 'create-account'; + +@Component({ + selector: 'app-login', + templateUrl: './login.page.html', + styleUrls: ['./login.page.scss'], +}) +export class LoginPage implements OnInit { + public step: LoginPageStep = 'greeter'; + @ViewChild('usernameInput') usernameInput: IonInput; + @ViewChild('passwordInput') passwordInput: IonInput; + @ViewChild('nameInput') nameInput: IonInput; + + stepMessages = { + username: 'Enter a username to continue', + password: 'Enter your password to sign-in', + 'create-account': 'Welcome! Create an account to continue', + }; + + public username = ''; + public password = ''; + public fullName = ''; + public passwordConfirm = ''; + public userExists?: boolean; + public userInfo: any = {}; + public errorMessage = ''; + + public readonly coreidUrl: string; + + constructor( + protected api: ApiService, + protected route: ActivatedRoute, + protected router: Router, + protected loader: LoadingController, + protected popover: PopoverController, + protected alerts: AlertController, + protected modals: ModalController, + public readonly editorService: EditorService, + public readonly navService: NavigationService, + public readonly auth: AuthService, + ) { + this.coreidUrl = this.api._build_url('/auth/starship_coreid/login', '/'); + } + + ngOnInit() {} + + async ionViewDidEnter() { + + } + + coreid() { + window.location.assign(this.coreidUrl); + } + + async back() { + this.errorMessage = ''; + + if ( this.step === 'password' ) { + this.password = ''; + this.step = 'username'; + + setTimeout(() => { + if ( this.usernameInput ) { + this.usernameInput.setFocus(); + } + }, 150); + } else if ( this.step === 'create-account' ) { + this.fullName = ''; + this.password = ''; + this.passwordConfirm = ''; + this.step = 'username'; + + setTimeout(() => { + if ( this.usernameInput ) { + this.usernameInput.setFocus(); + } + }, 150); + } + } + + async advance() { + this.errorMessage = ''; + + if ( this.step === 'greeter' ) { + this.step = 'username'; + + setTimeout(() => { + if ( this.usernameInput ) { + this.usernameInput.setFocus(); + } + }, 150); + } else if ( this.step === 'username' ) { + if ( !this.username ) { + return; + } + + const loader = await this.loader.create({ message: 'Checking username...' }); + await loader.present(); + + this.userInfo = await this.api.getUserInfo(this.username); + this.userExists = !!this.userInfo?.exists; + if ( this.userExists ) { + this.step = 'password'; + + setTimeout(() => { + if ( this.passwordInput ) { + this.passwordInput.setFocus(); + } + }, 150); + } else { + this.step = 'create-account'; + + setTimeout(() => { + if ( this.nameInput ) { + this.nameInput.setFocus(); + } + }, 150); + } + + await loader.dismiss(); + } else if ( this.step === 'password' ) { + if ( !this.username || !this.password ) { + return; + } + + const loader = await this.loader.create({ message: 'Signing you in...' }); + await loader.present(); + + const result = await this.api.attemptLogin(this.username, this.password); + await loader.dismiss(); + + if ( !result.success ) { + this.errorMessage = result.message || 'Un unknown error has occurred'; + } else { + this.auth.authInProgress = true; + this.navService.requestInitialization(); + this.reset(); + } + } else if ( this.step === 'create-account' ) { + if ( !this.username || !this.password || this.passwordConfirm !== this.password || !this.fullName ) { + return; + } + + const loader = await this.loader.create({ message: 'Creating your account...' }); + await loader.present(); + + const result = await this.api.attemptRegistration(this.username, this.password, this.passwordConfirm, this.fullName); + await loader.dismiss(); + + if ( !result.success ) { + this.errorMessage = result.message || 'Un unknown error has occurred'; + } else { + this.auth.authInProgress = true; + this.navService.requestInitialization(); + this.reset(); + } + } + } + + reset() { + this.username = ''; + this.password = ''; + this.passwordConfirm = ''; + this.fullName = ''; + this.step = 'greeter'; + } +} diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts index b605c09..27c9d81 100644 --- a/src/app/service/api.service.ts +++ b/src/app/service/api.service.ts @@ -1401,4 +1401,66 @@ export class ApiService { }); }); } + + public getUserInfo(uid: string): Promise { + return new Promise(async (res, rej) => { + if ( this.isOffline ) { + return rej(new ResourceNotAvailableOfflineError()); + } + + this.get('/auth/user-info', { uid }).subscribe({ + next: result => { + res(result.data); + }, + error: rej, + }); + }); + } + + public attemptLogin(uid: string, password: string): Promise<{ success: boolean, message?: string }> { + return new Promise(async (res, rej) => { + if ( this.isOffline ) { + return rej(new ResourceNotAvailableOfflineError()); + } + + this.post('/auth/attempt', { uid, password }).subscribe({ + next: result => { + res(result.data); + }, + error: rej, + }); + }); + } + + public attemptRegistration(uid: string, password: string, passwordConfirmation: string, fullName: string): + Promise<{ success: boolean, message?: string }> { + + return new Promise(async (res, rej) => { + if ( this.isOffline ) { + return rej(new ResourceNotAvailableOfflineError()); + } + + this.post('/auth/register', { uid, password, passwordConfirmation, fullName }).subscribe({ + next: result => { + res(result.data); + }, + error: rej, + }); + }); + } + + public endSession(): Promise { + return new Promise(async (res, rej) => { + if ( this.isOffline ) { + return rej(new ResourceNotAvailableOfflineError()); + } + + this.post('/auth/end-session').subscribe({ + next: result => { + return res(result.data); + }, + error: rej, + }); + }); + } } diff --git a/src/app/service/auth.service.ts b/src/app/service/auth.service.ts new file mode 100644 index 0000000..4085aab --- /dev/null +++ b/src/app/service/auth.service.ts @@ -0,0 +1,59 @@ +import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router'; +import {Injectable} from '@angular/core'; +import {ApiService} from './api.service'; +import {NavigationService} from './navigation.service'; +import {DatabaseService} from './db/database.service'; +import {LoadingController} from '@ionic/angular'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthService implements CanActivate { + public authInProgress = false; + + constructor( + protected readonly loader: LoadingController, + protected readonly api: ApiService, + protected readonly router: Router, + protected readonly nav: NavigationService, + protected readonly db: DatabaseService, + ) { } + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + const checkCanActivate = async () => { + const isAuthenticated = (this.api.isAuthenticated && !this.api.isPublicUser); + + if ( !isAuthenticated ) { + await this.router.navigate(['/login']); + return false; + } else { + return true; + } + }; + + return new Promise(async res => { + if ( !this.nav.initialized$.getValue() ) { + const sub = this.nav.initialized$.subscribe(async initialized => { + if ( initialized ) { + sub.unsubscribe(); + return res(await checkCanActivate()); + } + }); + } else { + return res(await checkCanActivate()); + } + }); + } + + async endSession() { + const loader = await this.loader.create({ message: 'Logging you out...' }); + await loader.present(); + + await this.db.purge(); + await this.api.endSession(); + await this.router.navigate(['/login']); + + await loader.dismiss(); + this.nav.requestInitialization(); + } +} diff --git a/src/app/service/guard/GuestOnly.guard.ts b/src/app/service/guard/GuestOnly.guard.ts new file mode 100644 index 0000000..bdf02a7 --- /dev/null +++ b/src/app/service/guard/GuestOnly.guard.ts @@ -0,0 +1,41 @@ +import {CanActivate, Router} from '@angular/router'; +import {ApiService} from '../api.service'; +import {NavigationService} from '../navigation.service'; +import {Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class GuestOnlyGuard implements CanActivate { + constructor( + protected readonly api: ApiService, + protected readonly router: Router, + protected readonly nav: NavigationService, + ) { } + + async canActivate(): Promise { + const checkCanActivate = async () => { + const isAuthenticated = (this.api.isAuthenticated && !this.api.isPublicUser); + + if ( isAuthenticated ) { + await this.router.navigate(['/home']); + return false; + } else { + return true; + } + }; + + return new Promise(async res => { + if ( !this.nav.initialized$.getValue() ) { + const sub = this.nav.initialized$.subscribe(async initialized => { + if ( initialized ) { + sub.unsubscribe(); + return res(await checkCanActivate()); + } + }); + } else { + return res(await checkCanActivate()); + } + }); + } +} diff --git a/src/app/service/navigation.service.ts b/src/app/service/navigation.service.ts index 20c7156..ce968ef 100644 --- a/src/app/service/navigation.service.ts +++ b/src/app/service/navigation.service.ts @@ -9,6 +9,8 @@ export class NavigationService { protected refreshCount = 0; public readonly sidebarRefresh$: BehaviorSubject<[number, boolean]> = new BehaviorSubject<[number, boolean]>([this.refreshCount, true]); public readonly navigationRequest$: BehaviorSubject = new BehaviorSubject(''); + public readonly initializationRequest$: BehaviorSubject = new BehaviorSubject(0); + public readonly initialized$: BehaviorSubject = new BehaviorSubject(false); requestSidebarRefresh({ quiet = false }) { this.refreshCount += 1; @@ -21,4 +23,9 @@ export class NavigationService { this.navigationRequest$.next(pageId); } + + requestInitialization() { + debug('Requesting application initialization'); + this.initializationRequest$.next(this.initializationRequest$.getValue() + 1); + } }