diff --git a/.drone.yml b/.drone.yml index 7beab32..b833ea8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -55,7 +55,7 @@ steps: - cd www - sed -i 's///' index.html - sed -i 's/Ionic App/Noded/' index.html - - echo -n '${DRONE_TAG}-staging' | sudo tee version.html + - echo -n "$(uuidgen)" | sudo tee version.html when: event: promote target: staging @@ -93,7 +93,7 @@ steps: - cd www - sed -i 's///' index.html - sed -i 's/Ionic App/Noded/' index.html - - echo -n '${DRONE_TAG}-prod' | sudo tee version.html + - echo -n "$(uuidgen)" | sudo tee version.html when: event: promote target: production diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6f90e65..2756d2a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -36,12 +36,7 @@ export class AppComponent implements OnInit { actionMapping: { mouse: { dblClick: (tree, node, $event) => { - const id = node.data.id; - const nodeId = node.data.node_id; - if ( !node.data.virtual ) { - this.currentPageId = id; - this.router.navigate(['/editor', { id, ...(nodeId ? { node_id: nodeId } : {}) }]); - } + this.navigateEditorToNode(node); }, click: (tree, node, $event) => { TREE_ACTIONS.FOCUS(tree, node, $event); @@ -165,6 +160,19 @@ export class AppComponent implements OnInit { this.hasSearchOpen = false; } + public navigateEditorToNode(node: any) { + if ( !node.data ) { + node = { data: node }; + } + + const id = node.data.id; + const nodeId = node.data.node_id; + if ( !node.data.virtual ) { + this.currentPageId = id; + this.router.navigate(['/editor', { id, ...(nodeId ? { node_id: nodeId } : {}) }]); + } + } + async onNodeMenuClick($event) { let canManage = this.menuTarget.data.level === 'manage'; if ( !canManage ) { @@ -368,6 +376,7 @@ export class AppComponent implements OnInit { } async initializeApp() { + console.log('session', this); this.loader = await this.loading.create({ message: 'Starting up...', cssClass: 'noded-loading-mask', @@ -388,17 +397,46 @@ export class AppComponent implements OnInit { this.session.systemBase = stat.system_base; await this.session.initialize(); + + if ( this.session.get('user.preferences.dark_mode') ) { + this.toggleDark(); + } + await this.statusBar.styleDefault(); await this.splashScreen.hide(); this.initialized$.next(true); + + if ( this.session.get('user.preferences.default_page') ) { + const id = this.session.get('user.preferences.default_page'); + const node = this.findNode(id); + if ( node ) { + this.navigateEditorToNode(node); + } + } } toggleDark() { // const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); this.darkMode = !this.darkMode; + this.session.set('user.preferences.dark_mode', this.darkMode); document.body.classList.toggle('dark', this.darkMode); } + findNode(id: string, nodes = this.nodes) { + for ( const node of nodes ) { + if ( node.id === id ) { + return node; + } + + if ( node.children ) { + const foundNode = this.findNode(id, node.children); + if ( foundNode ) { + return foundNode; + } + } + } + } + isDark() { return !!this.darkMode; } diff --git a/src/app/service/session.service.ts b/src/app/service/session.service.ts index a2a9062..c8c76d9 100644 --- a/src/app/service/session.service.ts +++ b/src/app/service/session.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import {ApiService} from './api.service'; +import {debounce} from './editor.service'; @Injectable({ providedIn: 'root' @@ -8,6 +9,22 @@ export class SessionService { public appName!: string; public systemBase!: string; protected data: any = {}; + protected saveTriggered = false; + protected saving = false; + protected privTriggerSave = debounce(() => { + if ( this.saving ) { + this.triggerSave(); + } else { + this.save(); + } + + this.saveTriggered = false; + }, 3000); + + public triggerSave() { + this.saveTriggered = true; + this.privTriggerSave(); + } constructor( protected readonly api: ApiService, @@ -30,6 +47,22 @@ export class SessionService { }); } + async save() { + this.saving = true; + return new Promise((res, rej) => { + this.api.post('/session', this.data || {}).subscribe({ + next: result => { + res(); + this.saving = false; + }, + error: (e) => { + this.saving = false; + rej(e); + }, + }); + }); + } + buildAppUrl(...parts: string[]): string { parts = parts.map(x => { if ( x.startsWith('/') ) { @@ -45,4 +78,45 @@ export class SessionService { return `${this.systemBase}${this.systemBase.endsWith('/') ? '' : '/'}${parts.join('/')}`; } + + get(path?: string): any { + let current: any = this.data; + if ( !path ) { + return current; + } + + const parts = path.split('.'); + for ( const part of parts ) { + current = current?.[part]; + } + + return current; + } + + set(pathOrValue: string | any, value?: any): any { + if ( typeof pathOrValue !== 'string' ) { + this.data = pathOrValue; + return; + } + + if ( typeof value === 'undefined' ) { + return; + } + + const parts = pathOrValue.split('.'); + let last; + let current = this.data; + + for ( const part of parts ) { + last = current; + current = current?.[part]; + } + + parts.reverse(); + if ( last ) { + last[parts[0]] = value; + } + + this.triggerSave(); + } }