Centralize opener logic to shared service
This commit is contained in:
parent
0fecb8a4ba
commit
72ab2064dd
@ -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 }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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: [
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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> {
|
||||||
|
@ -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]);
|
||||||
|
81
src/app/service/opener.service.ts
Normal file
81
src/app/service/opener.service.ts
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user