Centralize opener logic to shared service

This commit is contained in:
Garrett Mills 2021-02-15 19:07:35 -06:00
parent 0fecb8a4ba
commit 72ab2064dd
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
9 changed files with 161 additions and 100 deletions

View File

@ -25,6 +25,7 @@ import {DatabaseService} from './service/db/database.service';
import {EditorService} from './service/editor.service'; import {EditorService} from './service/editor.service';
import {debug} from './utility'; import {debug} from './utility';
import {AuthService} from './service/auth.service'; import {AuthService} from './service/auth.service';
import {OpenerService} from './service/opener.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -115,6 +116,7 @@ export class AppComponent implements OnInit {
protected db: DatabaseService, protected db: DatabaseService,
protected editor: EditorService, protected editor: EditorService,
protected auth: AuthService, protected auth: AuthService,
protected opener: OpenerService,
) { } ) { }
async checkNewVersion() { async checkNewVersion() {
@ -205,8 +207,8 @@ export class AppComponent implements OnInit {
if ( !node.data.virtual ) { if ( !node.data.virtual ) {
debug('Navigating editor to node:', {id, nodeId}); debug('Navigating editor to node:', {id, nodeId});
this.currentPageId = id; this.opener.currentPageId = id;
this.router.navigate(['/editor', { id, ...(nodeId ? { node_id: nodeId } : {}) }]); this.opener.openTarget(id, nodeId);
} }
} }
@ -438,7 +440,7 @@ export class AppComponent implements OnInit {
this.api this.api
.post(`/page/delete/${this.deleteTarget.data.id}`) .post(`/page/delete/${this.deleteTarget.data.id}`)
.subscribe(res => { .subscribe(res => {
if ( this.currentPageId === this.deleteTarget.data.id ) { if ( this.opener.currentPageId === this.deleteTarget.data.id ) {
this.router.navigate(['/home']); this.router.navigate(['/home']);
} }
@ -630,7 +632,7 @@ export class AppComponent implements OnInit {
return; return;
} }
this.currentPageId = pageId; this.opener.currentPageId = pageId;
this.router.navigate(['/editor', { id: pageId }]); this.router.navigate(['/editor', { id: pageId }]);
}); });

View File

@ -5,7 +5,12 @@ import {EditorService} from '../../../service/editor.service';
selector: 'noded-file-box-page', selector: 'noded-file-box-page',
template: ` template: `
<div class="container" *ngIf="ready"> <div class="container" *ngIf="ready">
<editor-file-box [nodeId]="nodeId" [editorUUID]="this.editorService.instanceUUID" [fullPage]="true" [boxId]="boxId"></editor-file-box> <editor-file-box
[nodeId]="nodeId"
[editorUUID]="this.editorService.instanceUUID"
[fullPage]="true"
[boxId]="boxId"
></editor-file-box>
</div> </div>
`, `,
styles: [ styles: [

View File

@ -101,6 +101,10 @@ div.file-box-wrapper {
} }
} }
.close {
padding: 0 20px;
}
.file-box-wrapper.dark { .file-box-wrapper.dark {
color: white; color: white;
display: flex; display: flex;
@ -115,10 +119,6 @@ div.file-box-wrapper {
} }
} }
.close {
padding: 0 20px;
}
.item { .item {
background: #393939; background: #393939;

View File

@ -5,8 +5,7 @@ import {BehaviorSubject} from 'rxjs';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {NodeTypeIcons} from '../../structures/node-types'; import {NodeTypeIcons} from '../../structures/node-types';
import {debounce} from '../../utility'; import {debounce} from '../../utility';
import {DatabasePageComponent} from '../editor/database/database-page.component'; import {OpenerService} from '../../service/opener.service';
import {FileBoxPageComponent} from '../nodes/file-box/file-box-page.component';
export interface SearchResult { export interface SearchResult {
title: string; title: string;
@ -44,6 +43,7 @@ export class SearchComponent implements OnInit {
protected ionModalController: ModalController, protected ionModalController: ModalController,
protected api: ApiService, protected api: ApiService,
protected router: Router, protected router: Router,
protected opener: OpenerService,
) { } ) { }
async dismiss() { async dismiss() {
@ -75,15 +75,15 @@ export class SearchComponent implements OnInit {
]; ];
if ( result.type === 'page' ) { if ( result.type === 'page' ) {
await this.router.navigate(['/editor', { id: result.id }]); await this.opener.openTarget(result.id);
await this.dismiss(); await this.dismiss();
} else if ( nodeTypes.includes(result.type) ) { } 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(); await this.dismiss();
} else if ( result.type === 'db' ) { } 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') ) { } 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 ) {
if ( result.associated.type === 'page' ) { if ( result.associated.type === 'page' ) {
await this.router.navigate(['/editor', { id: result.associated.id }]); await this.opener.openTarget(result.associated.id);
await this.dismiss(); 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();
}
} }

View File

@ -14,6 +14,7 @@ import {FileGroup} from './db/FileGroup';
import {Page} from './db/Page'; import {Page} from './db/Page';
import {PageNode} from './db/PageNode'; import {PageNode} from './db/PageNode';
import {debug} from '../utility'; import {debug} from '../utility';
import HostRecord from '../structures/HostRecord';
export class ResourceNotAvailableOfflineError extends Error { export class ResourceNotAvailableOfflineError extends Error {
constructor(msg = 'This resource is not yet available offline on this device.') { 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<HostRecord[]> {
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<void> { public deleteCodium(PageId: string, NodeId: string, CodiumId: string): Promise<void> {
return new Promise(async (res, rej) => { return new Promise(async (res, rej) => {
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray(); const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();

View File

@ -705,48 +705,7 @@ export class EditorService {
} }
async loadNodes(pageId: string, version?: number): Promise<HostRecord[]> { async loadNodes(pageId: string, version?: number): Promise<HostRecord[]> {
return new Promise(async (res, rej) => { return this.api.loadNodes(pageId, version);
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,
});
});
} }
public createPage(name: string): Promise<any> { public createPage(name: string): Promise<any> {

View File

@ -1,6 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import {BehaviorSubject} from 'rxjs'; import {BehaviorSubject} from 'rxjs';
import {debug} from '../utility'; 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({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -12,6 +16,11 @@ export class NavigationService {
public readonly initializationRequest$: BehaviorSubject<number> = new BehaviorSubject<number>(0); public readonly initializationRequest$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
public readonly initialized$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); public readonly initialized$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
constructor(
protected readonly ionModalController: ModalController,
protected readonly api: ApiService,
) { }
requestSidebarRefresh({ quiet = false }) { requestSidebarRefresh({ quiet = false }) {
this.refreshCount += 1; this.refreshCount += 1;
this.sidebarRefresh$.next([this.refreshCount, quiet]); this.sidebarRefresh$.next([this.refreshCount, quiet]);

View File

@ -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();
}
}

View File

@ -1,7 +1,7 @@
export default class HostRecord { export default class HostRecord {
public value = ''; public value = '';
// tslint:disable-next-line:max-line-length // 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 CreatedAt: string;
public PageId: string; public PageId: string;