diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1aad1e8..c53f12d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -25,6 +25,7 @@ import {DatabaseService} from './service/db/database.service'; import {EditorService} from './service/editor.service'; import {debug} from './utility'; import {AuthService} from './service/auth.service'; +import {OpenerService} from './service/opener.service'; @Component({ selector: 'app-root', @@ -115,6 +116,7 @@ export class AppComponent implements OnInit { protected db: DatabaseService, protected editor: EditorService, protected auth: AuthService, + protected opener: OpenerService, ) { } async checkNewVersion() { @@ -205,8 +207,8 @@ export class AppComponent implements OnInit { if ( !node.data.virtual ) { debug('Navigating editor to node:', {id, nodeId}); - this.currentPageId = id; - this.router.navigate(['/editor', { id, ...(nodeId ? { node_id: nodeId } : {}) }]); + this.opener.currentPageId = id; + this.opener.openTarget(id, nodeId); } } @@ -438,7 +440,7 @@ export class AppComponent implements OnInit { this.api .post(`/page/delete/${this.deleteTarget.data.id}`) .subscribe(res => { - if ( this.currentPageId === this.deleteTarget.data.id ) { + if ( this.opener.currentPageId === this.deleteTarget.data.id ) { this.router.navigate(['/home']); } @@ -630,7 +632,7 @@ export class AppComponent implements OnInit { return; } - this.currentPageId = pageId; + this.opener.currentPageId = pageId; this.router.navigate(['/editor', { id: pageId }]); }); diff --git a/src/app/components/nodes/file-box/file-box-page.component.ts b/src/app/components/nodes/file-box/file-box-page.component.ts index 4edc8e4..acedb63 100644 --- a/src/app/components/nodes/file-box/file-box-page.component.ts +++ b/src/app/components/nodes/file-box/file-box-page.component.ts @@ -5,7 +5,12 @@ import {EditorService} from '../../../service/editor.service'; selector: 'noded-file-box-page', template: `
- +
`, styles: [ diff --git a/src/app/components/nodes/file-box/file-box.component.scss b/src/app/components/nodes/file-box/file-box.component.scss index ef4723b..3b99ed8 100644 --- a/src/app/components/nodes/file-box/file-box.component.scss +++ b/src/app/components/nodes/file-box/file-box.component.scss @@ -101,6 +101,10 @@ div.file-box-wrapper { } } +.close { + padding: 0 20px; +} + .file-box-wrapper.dark { color: white; display: flex; @@ -115,10 +119,6 @@ div.file-box-wrapper { } } - .close { - padding: 0 20px; - } - .item { background: #393939; diff --git a/src/app/components/search/Search.component.ts b/src/app/components/search/Search.component.ts index f41f4f3..e1745eb 100644 --- a/src/app/components/search/Search.component.ts +++ b/src/app/components/search/Search.component.ts @@ -5,8 +5,7 @@ import {BehaviorSubject} from 'rxjs'; import {Router} from '@angular/router'; import {NodeTypeIcons} from '../../structures/node-types'; import {debounce} from '../../utility'; -import {DatabasePageComponent} from '../editor/database/database-page.component'; -import {FileBoxPageComponent} from '../nodes/file-box/file-box-page.component'; +import {OpenerService} from '../../service/opener.service'; export interface SearchResult { title: string; @@ -44,6 +43,7 @@ export class SearchComponent implements OnInit { protected ionModalController: ModalController, protected api: ApiService, protected router: Router, + protected opener: OpenerService, ) { } async dismiss() { @@ -75,15 +75,15 @@ export class SearchComponent implements OnInit { ]; if ( result.type === 'page' ) { - await this.router.navigate(['/editor', { id: result.id }]); + await this.opener.openTarget(result.id); await this.dismiss(); } else if ( nodeTypes.includes(result.type) ) { - await this.router.navigate(['/editor', { id: result.associated.id, node_id: result.id }]); + await this.opener.openTarget(result.associated.id, result.id); await this.dismiss(); } else if ( result.type === 'db' ) { - await this.openDatabase(result.associated.id, result.id); + await this.opener.openDatabase(result.associated.id, result.id); } else if ( result.type.startsWith('file_box') ) { - await this.openFileBox(result.associated.id, result.id, result.boxId); + await this.opener.openFileBox(result.associated.id, result.id, result.boxId); } } @@ -93,50 +93,9 @@ export class SearchComponent implements OnInit { if ( result.associated ) { if ( result.associated.type === 'page' ) { - await this.router.navigate(['/editor', { id: result.associated.id }]); + await this.opener.openTarget(result.associated.id); await this.dismiss(); } } } - - async openDatabase(pageId: string, nodeId: string) { - const modal = await this.ionModalController.create({ - component: DatabasePageComponent, - componentProps: { - nodeId, - pageId, - }, - cssClass: 'modal-big', - }); - - const modalState = { - modal : true, - desc : 'Open Database' - }; - - history.pushState(modalState, null); - - await modal.present(); - } - - async openFileBox(pageId: string, nodeId: string, boxId?: string) { - const modal = await this.ionModalController.create({ - component: FileBoxPageComponent, - componentProps: { - nodeId, - pageId, - boxId, - }, - cssClass: 'modal-big', - }); - - const modalState = { - modal : true, - desc : 'Open File Box' - }; - - history.pushState(modalState, null); - - await modal.present(); - } } diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts index 27c9d81..cfae368 100644 --- a/src/app/service/api.service.ts +++ b/src/app/service/api.service.ts @@ -14,6 +14,7 @@ import {FileGroup} from './db/FileGroup'; import {Page} from './db/Page'; import {PageNode} from './db/PageNode'; import {debug} from '../utility'; +import HostRecord from '../structures/HostRecord'; export class ResourceNotAvailableOfflineError extends Error { constructor(msg = 'This resource is not yet available offline on this device.') { @@ -647,6 +648,51 @@ export class ApiService { }); } + public async loadNodes(pageId: string, version?: number): Promise { + return new Promise(async (res, rej) => { + const existingNodes = await this.db.pageNodes.where({ PageId: pageId }).toArray() as PageNode[]; + + const inflateRecords = (records) => { + return records.map(rec => { + const host = new HostRecord(rec.Value.Value); + host.load(rec); + return host; + }); + }; + + // If we're offline, just resolve the offline nodes + if ( this.isOffline ) { + const parsedRecords = existingNodes.map(x => x.inflateToRecord()); + return res(inflateRecords(parsedRecords)); + } + + this.get(`/page/${pageId}/nodes${version ? '?version=' + version : ''}`).subscribe({ + next: async result => { + // If we got resolved records, delete the local ones to replace them + await this.db.pageNodes.where({ PageId: pageId }).delete(); + + for ( const rawRec of result.data ) { + const newLocalNode = new PageNode( + rawRec.UUID, + rawRec.Type, + JSON.stringify(rawRec.Value), + rawRec.PageId, + rawRec.CreatedAt, + rawRec.UpdatedAt, + rawRec.CreatedUserId, + rawRec.UpdateUserId, + ); + + await newLocalNode.save(); + } + + res(inflateRecords(result.data)); + }, + error: rej, + }); + }); + } + public deleteCodium(PageId: string, NodeId: string, CodiumId: string): Promise { return new Promise(async (res, rej) => { const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray(); diff --git a/src/app/service/editor.service.ts b/src/app/service/editor.service.ts index a83d87e..52acf06 100644 --- a/src/app/service/editor.service.ts +++ b/src/app/service/editor.service.ts @@ -705,48 +705,7 @@ export class EditorService { } async loadNodes(pageId: string, version?: number): Promise { - return new Promise(async (res, rej) => { - const existingNodes = await this.db.pageNodes.where({ PageId: pageId }).toArray() as PageNode[]; - - const inflateRecords = (records) => { - return records.map(rec => { - const host = new HostRecord(rec.Value.Value); - host.load(rec); - return host; - }); - }; - - // If we're offline, just resolve the offline nodes - if ( this.api.isOffline ) { - const parsedRecords = existingNodes.map(x => x.inflateToRecord()); - return res(inflateRecords(parsedRecords)); - } - - this.api.get(`/page/${pageId}/nodes${version ? '?version=' + version : ''}`).subscribe({ - next: async result => { - // If we got resolved records, delete the local ones to replace them - await this.db.pageNodes.where({ PageId: pageId }).delete(); - - for ( const rawRec of result.data ) { - const newLocalNode = new PageNode( - rawRec.UUID, - rawRec.Type, - JSON.stringify(rawRec.Value), - rawRec.PageId, - rawRec.CreatedAt, - rawRec.UpdatedAt, - rawRec.CreatedUserId, - rawRec.UpdateUserId, - ); - - await newLocalNode.save(); - } - - res(inflateRecords(result.data)); - }, - error: rej, - }); - }); + return this.api.loadNodes(pageId, version); } public createPage(name: string): Promise { diff --git a/src/app/service/navigation.service.ts b/src/app/service/navigation.service.ts index ce968ef..9619f4d 100644 --- a/src/app/service/navigation.service.ts +++ b/src/app/service/navigation.service.ts @@ -1,6 +1,10 @@ import { Injectable } from '@angular/core'; import {BehaviorSubject} from 'rxjs'; import {debug} from '../utility'; +import {DatabasePageComponent} from '../components/editor/database/database-page.component'; +import {FileBoxPageComponent} from '../components/nodes/file-box/file-box-page.component'; +import {ModalController} from '@ionic/angular'; +import {ApiService} from './api.service'; @Injectable({ providedIn: 'root' @@ -12,6 +16,11 @@ export class NavigationService { public readonly initializationRequest$: BehaviorSubject = new BehaviorSubject(0); public readonly initialized$: BehaviorSubject = new BehaviorSubject(false); + constructor( + protected readonly ionModalController: ModalController, + protected readonly api: ApiService, + ) { } + requestSidebarRefresh({ quiet = false }) { this.refreshCount += 1; this.sidebarRefresh$.next([this.refreshCount, quiet]); diff --git a/src/app/service/opener.service.ts b/src/app/service/opener.service.ts new file mode 100644 index 0000000..712ed85 --- /dev/null +++ b/src/app/service/opener.service.ts @@ -0,0 +1,81 @@ +import {Injectable} from '@angular/core'; +import {DatabasePageComponent} from '../components/editor/database/database-page.component'; +import {FileBoxPageComponent} from '../components/nodes/file-box/file-box-page.component'; +import {NavigationService} from './navigation.service'; +import {EditorService} from './editor.service'; +import {ModalController} from '@ionic/angular'; + +@Injectable({ + providedIn: 'root', +}) +export class OpenerService { + public currentPageId = ''; + + constructor( + protected readonly nav: NavigationService, + protected readonly editor: EditorService, + protected readonly ionModalController: ModalController, + ) { } + + async openTarget(pageId: string, nodeId?: string) { + if ( !nodeId ) { + this.currentPageId = pageId; + return this.nav.requestNavigation(pageId); + } + + const node = (await this.editor.loadNodes(pageId)).find(rec => rec.UUID === nodeId); + if ( !node ) { + this.currentPageId = pageId; + return this.nav.requestNavigation(pageId); + } + + if ( node.type === 'database_ref' ) { + return this.openDatabase(pageId, nodeId); + } + + if ( node.type === 'file_box' ) { + return this.openFileBox(pageId, nodeId); + } + } + + async openDatabase(pageId: string, nodeId: string) { + const modal = await this.ionModalController.create({ + component: DatabasePageComponent, + componentProps: { + nodeId, + pageId, + }, + cssClass: 'modal-big', + }); + + const modalState = { + modal : true, + desc : 'Open Database' + }; + + history.pushState(modalState, null); + + await modal.present(); + } + + async openFileBox(pageId: string, nodeId: string, boxId?: string) { + const modal = await this.ionModalController.create({ + component: FileBoxPageComponent, + componentProps: { + nodeId, + pageId, + boxId, + }, + cssClass: 'modal-big', + }); + + const modalState = { + modal : true, + desc : 'Open File Box' + }; + + history.pushState(modalState, null); + + await modal.present(); + } +} diff --git a/src/app/structures/HostRecord.ts b/src/app/structures/HostRecord.ts index 291a00e..c6a1877 100644 --- a/src/app/structures/HostRecord.ts +++ b/src/app/structures/HostRecord.ts @@ -1,7 +1,7 @@ export default class HostRecord { public value = ''; // tslint:disable-next-line:max-line-length - public type: 'paragraph'|'database_ref'|'code_ref'|'file_ref'|'markdown'|'form_input_text'|'form_input_number'|'form_input_password'|'form_input_email'|'form_input_select'|'form_input_multiselect'|'form_input_textarea' = 'paragraph'; + public type: 'paragraph'|'database_ref'|'code_ref'|'file_ref'|'markdown'|'form_input_text'|'form_input_number'|'form_input_password'|'form_input_email'|'form_input_select'|'form_input_multiselect'|'form_input_textarea'|'file_box' = 'paragraph'; public CreatedAt: string; public PageId: string;