diff --git a/src/app/app.component.html b/src/app/app.component.html index 1b29ed9..88f2a69 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,6 @@ - + {{ appName }} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9556271..add8854 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener, Host} from '@angular/core'; +import {Component, OnInit, ViewChild, HostListener, Host} from '@angular/core'; import { AlertController, @@ -23,6 +23,7 @@ import {NodeTypeIcons} from './structures/node-types'; import {NavigationService} from './service/navigation.service'; import {DatabaseService} from './service/db/database.service'; import {EditorService} from './service/editor.service'; +import {debug} from './utility'; @Component({ selector: 'app-root', @@ -88,7 +89,7 @@ export class AppComponent implements OnInit { private platform: Platform, private splashScreen: SplashScreen, private statusBar: StatusBar, - private api: ApiService, + public readonly api: ApiService, protected router: Router, protected alerts: AlertController, protected popover: PopoverController, @@ -99,27 +100,7 @@ export class AppComponent implements OnInit { protected toasts: ToastController, protected db: DatabaseService, protected editor: EditorService, - ) { - this.initializeApp(); - } - - async _doInit() { - await this.db.createSchemata(); - - this.reloadMenuItems().subscribe(() => { - this.ready$.next(true); - setTimeout(() => { - this.loader.dismiss(); - this.menuTree.treeModel.expandAll(); - }, 10); - - if ( !this.versionInterval ) { - this.versionInterval = setInterval(() => { - this.checkNewVersion(); - }, 1000 * 60 * 5); // Check for new version every 5 mins - } - }); - } + ) { } async checkNewVersion() { if ( !this.showedNewVersionAlert && await this.session.newVersionAvailable() ) { @@ -144,20 +125,8 @@ export class AppComponent implements OnInit { } ngOnInit() { - if ( !this.initialized$.getValue() ) { - this.navService.sidebarRefresh$.subscribe(([_, quiet]) => { - this.onMenuRefresh(quiet); - }); - - const sub = this.initialized$.subscribe((didInit) => { - if (didInit) { - this._doInit(); - sub.unsubscribe(); - } - }); - } else { - this._doInit(); - } + debug('Initializing application.'); + this.initializeApp(); } showOptions($event) { @@ -262,7 +231,7 @@ export class AppComponent implements OnInit { }); } else if ( result.data === 'export_html' ) { this.exportTargetAsHTML(); - }; + } }); await popover.present(); @@ -420,19 +389,24 @@ export class AppComponent implements OnInit { } async initializeApp() { - console.log('app', this); + debug('app', this); this.loader = await this.loading.create({ message: 'Starting up...', cssClass: 'noded-loading-mask', showBackdrop: true, }); + debug('Initializing platform and database...'); await this.loader.present(); await this.platform.ready(); + await this.db.createSchemata(); 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...', @@ -441,39 +415,64 @@ export class AppComponent implements OnInit { 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.systemBase = stat.system_base; - if ( !stat.authenticated_user ) { + if ( !this.api.isAuthenticated || this.api.isPublicUser ) { + debug('Unauthenticated or public user...'); if ( !this.api.isOffline ) { + debug('Trying to resume session...'); await this.api.resumeSession(); + debug('Checking new status...'); stat = await this.session.stat(); + debug('Got session resume stat:', stat); + + this.api.isAuthenticated = stat.authenticated_user; + this.api.isPublicUser = stat.public_user; if ( !stat.authenticated_user ) { + debug('Not authenticated! Redirecting.'); window.location.href = `${stat.system_base}start`; return; } } else { + debug('Unauthenticated offline user. Purging local data!'); await this.db.purge(); window.location.href = `${stat.system_base}start`; return; } } + debug('Set app name and system base:', stat.app_name, stat.system_base); this.session.appName = stat.app_name; this.session.systemBase = stat.system_base; + debug('Initializing session...'); await this.session.initialize(); if ( this.session.get('user.preferences.dark_mode') ) { this.toggleDark(); } + debug('Hiding native splash screen & setting status bar styles...'); await this.statusBar.styleDefault(); await this.splashScreen.hide(); @@ -495,11 +494,13 @@ export class AppComponent implements OnInit { } } - if ( this.isPrefetch() ) { - this.loader.message = 'Downloading data...'; // TODO actually do the prefetch + if ( this.isPrefetch() && !this.api.isPublicUser ) { + debug('Pre-fetching offline data...'); + this.loader.message = 'Downloading data...'; try { await this.api.prefetchOfflineData(); } catch (e) { + debug('Pre-fetch error:', e); this.toasts.create({ cssClass: 'compat-toast-container', message: 'An error occurred while pre-fetching offline data. Not all data was saved.', @@ -514,13 +515,37 @@ export class AppComponent implements OnInit { this.initialized$.next(true); - if ( this.session.get('user.preferences.default_page') ) { + if ( !this.api.isPublicUser && this.session.get('user.preferences.default_page') ) { + debug('Navigating to default page!'); const id = this.session.get('user.preferences.default_page'); const node = this.findNode(id); if ( node ) { this.navigateEditorToNode(node); } } + + debug('Creating menu subscription...'); + this.navService.sidebarRefresh$.subscribe(([_, quiet]) => { + this.onMenuRefresh(quiet); + }); + + debug('Reloading menu items...'); + this.reloadMenuItems().subscribe(() => { + debug('Reloaded menu items. Displaying interface.'); + this.ready$.next(true); + + setTimeout(() => { + this.loader.dismiss(); + this.menuTree?.treeModel?.expandAll(); + }, 10); + + if ( !this.versionInterval ) { + this.versionInterval = setInterval(() => { + debug('Checking for new application version.'); + this.checkNewVersion(); + }, 1000 * 60 * 5); // Check for new version every 5 mins + } + }); } async doPrefetch() { diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index 0a232f0..5ec3a0a 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -8,12 +8,14 @@ - - + Hi, there! Select or create a page to get started. - + (You can press Ctrl + / to search everywhere.) + + Redirecting... + diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index d591c51..ac1557d 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -1,15 +1,28 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import {ApiService} from '../service/api.service'; +import {isDebug, debug} from '../utility'; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) -export class HomePage { +export class HomePage implements OnInit { constructor( - protected api: ApiService, + public readonly api: ApiService, ) {} + ngOnInit() { + if ( !this.api.isAuthenticated || this.api.isPublicUser ) { + if ( isDebug() ) { + debug('Forcing authentication...'); + setTimeout(() => { + this.api.forceRestart(); + }, 2000); + } else { + this.api.forceRestart(); + } + } + } } diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts index 8e7b7b5..4067e93 100644 --- a/src/app/service/api.service.ts +++ b/src/app/service/api.service.ts @@ -13,6 +13,7 @@ import {DatabaseEntry} from './db/DatabaseEntry'; import {FileGroup} from './db/FileGroup'; import {Page} from './db/Page'; import {PageNode} from './db/PageNode'; +import {debug} from '../utility'; export class ResourceNotAvailableOfflineError extends Error { constructor(msg = 'This resource is not yet available offline on this device.') { @@ -30,6 +31,9 @@ export class OfflinePrefetchWouldOverwriteLocalDataError extends Error { providedIn: 'root' }) export class ApiService { + public isAuthenticated = false; + public isPublicUser = false; + public systemBase?: string; protected baseEndpoint: string = environment.backendBase; protected statUrl: string = environment.statUrl; protected versionUrl: string = environment.versionUrl; @@ -149,6 +153,10 @@ export class ApiService { }); } + public forceRestart() { + window.location.href = `${this.systemBase || '/'}start`; + } + public checkOnline(): Promise { return new Promise(res => { fetch(this.statUrl).then(resp => { @@ -170,6 +178,10 @@ export class ApiService { } public async needsSync() { + if ( !this.isAuthenticated || this.isPublicUser ) { + return false; + } + const offlineKV = await this.db.getKeyValue('needs_online_sync'); return Boolean(offlineKV.data); } @@ -456,6 +468,10 @@ export class ApiService { public getDeviceToken(): Promise { return new Promise(async (res, rej) => { + if ( this.isPublicUser ) { + return res(); + } + const tokenKV = await this.db.getKeyValue('device_token'); if ( !tokenKV.data && this.isOffline ) { return rej(new ResourceNotAvailableOfflineError()); @@ -483,6 +499,11 @@ export class ApiService { return new Promise(async (res, rej) => { if ( !this.isOffline ) { this.getDeviceToken().then(token => { + debug('Got device token:', token); + if ( !token ) { + return res(); + } + this.post(`/session/resume/${token}`).subscribe({ next: result => { res(); diff --git a/src/app/utility.ts b/src/app/utility.ts index 9fdc206..c5a2d3b 100644 --- a/src/app/utility.ts +++ b/src/app/utility.ts @@ -1,3 +1,4 @@ +import {environment} from '../environments/environment'; export function uuid_v4() { // @ts-ignore @@ -17,3 +18,29 @@ export function debounce(func: (...args: any[]) => any, timeout?: number) { timer = setTimeout(next, timeout > 0 ? timeout : 300); }; } + +export function debugRun(closure: any) { + if ( typeof closure === 'function' ) { + return closure(); + } +} + +export function isDebug() { + return environment.outputDebug; +} + +export function debug(...out: any[]) { + const caller = (new Error())?.stack?.split('\n')?.[1]?.split('@')?.[0]?.replace(/[\/\\<]/g, ''); + + // Define different types of styles + const baseStyles = [ + 'color: #fff', + 'background-color: #449', + 'padding: 2px 4px', + 'border-radius: 2px' + ].join(';'); + + if ( environment.outputDebug ) { + console.log(`%c${caller}`, baseStyles, ...out); + } +} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 71ae324..345f10a 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -3,4 +3,5 @@ export const environment = { backendBase: '/api/v1', statUrl: '/stat?ngsw-bypass', versionUrl: '/i/version.html?ngsw-bypass', + outputDebug: false, }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index aed2730..4e6eb22 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -7,6 +7,7 @@ export const environment = { backendBase: '/link_api/api/v1', statUrl: '/link_api/stat?ngsw-bypass', versionUrl: '/link_api/assets/version.html?ngsw-bypass', + outputDebug: true, }; /*