Cache pages and page nodes for offline use
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
380a139de3
commit
fe7e955875
15
.drone.yml
15
.drone.yml
@ -128,18 +128,3 @@ steps:
|
|||||||
}
|
}
|
||||||
when:
|
when:
|
||||||
status: failure
|
status: failure
|
||||||
|
|
||||||
# ================ DEPLOY STAGING =====================
|
|
||||||
- name: promote staging build
|
|
||||||
image: plugins/downstream
|
|
||||||
settings:
|
|
||||||
server: https://ci.garrettmills.dev
|
|
||||||
token:
|
|
||||||
from_secret: drone_token
|
|
||||||
fork: false
|
|
||||||
last_successful: true
|
|
||||||
deploy: staging
|
|
||||||
repositories:
|
|
||||||
- Noded/frontend@master
|
|
||||||
when:
|
|
||||||
status: success
|
|
@ -21,7 +21,12 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ng-container>
|
<ng-container *ngIf="editorService.notAvailable">
|
||||||
|
<div class="editor-root ion-padding" style="text-align: center; padding-top: 100px; color: #494949; font-size: 1.2em;">
|
||||||
|
Sorry, this page is not available offline yet.
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!editorService.notAvailable">
|
||||||
<div class="editor-root ion-padding">
|
<div class="editor-root ion-padding">
|
||||||
<div
|
<div
|
||||||
class="host-container"
|
class="host-container"
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import {Component, Host, HostListener, Input, OnInit, ViewChild, ViewChildren} from '@angular/core';
|
import {Component, HostListener, Input, OnInit} from '@angular/core';
|
||||||
import HostRecord from '../../structures/HostRecord';
|
import HostRecord from '../../structures/HostRecord';
|
||||||
import PageRecord from '../../structures/PageRecord';
|
|
||||||
import {PageService} from '../../service/page.service';
|
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {LoadingController, PopoverController} from '@ionic/angular';
|
import {LoadingController, PopoverController} from '@ionic/angular';
|
||||||
import {NodePickerComponent} from '../../components/editor/node-picker/node-picker.component';
|
import {NodePickerComponent} from '../../components/editor/node-picker/node-picker.component';
|
||||||
@ -15,13 +13,8 @@ import {NodeTypeIcons} from '../../structures/node-types';
|
|||||||
styleUrls: ['./editor.page.scss'],
|
styleUrls: ['./editor.page.scss'],
|
||||||
})
|
})
|
||||||
export class EditorPage implements OnInit {
|
export class EditorPage implements OnInit {
|
||||||
// @ViewChildren('editorHosts') editorHosts;
|
|
||||||
// @ViewChild('titleBar') titleBar;
|
|
||||||
|
|
||||||
public typeIcons = NodeTypeIcons;
|
public typeIcons = NodeTypeIcons;
|
||||||
|
|
||||||
@Input() pageId: string;
|
@Input() pageId: string;
|
||||||
public pageName = '';
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
@ -96,7 +89,6 @@ export class EditorPage implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
popover.onDidDismiss().then(result => {
|
popover.onDidDismiss().then(result => {
|
||||||
console.log('adding node', result.data);
|
|
||||||
if ( !result.data ) {
|
if ( !result.data ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -104,238 +96,6 @@ export class EditorPage implements OnInit {
|
|||||||
this.editorService.addNode(result.data, position, positionNodeId);
|
this.editorService.addNode(result.data, position, positionNodeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// popover.onDidDismiss().then(arg => {
|
|
||||||
// const defValue = this.getDefaultValue(arg.data);
|
|
||||||
// const hostRec = new HostRecord(defValue);
|
|
||||||
// hostRec.type = arg.data;
|
|
||||||
// hostRec.PageId = this.pageRecord.UUID;
|
|
||||||
//
|
|
||||||
// if ( hostRec.type === 'ul' ) {
|
|
||||||
// hostRec.value = JSON.stringify([{value: '', indentationLevel: 0}]);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.hostRecords.push(hostRec);
|
|
||||||
// if ( hostRec.isNorm() ) {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// this.editorHosts.toArray().reverse()[0].takeFocus();
|
|
||||||
// }, 0);
|
|
||||||
// } else {
|
|
||||||
// this.onSaveClick();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
await popover.present();
|
await popover.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
// buttonIsVisible(index) {
|
|
||||||
// return this.visibleButtons.includes(index);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// makeVisible(index) {
|
|
||||||
// if ( !this.buttonIsVisible(index) ) {
|
|
||||||
// this.visibleButtons.push(index);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// makeInvisible(index) {
|
|
||||||
// this.visibleButtons = this.visibleButtons.filter(x => x !== index);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ionViewDidEnter() {
|
|
||||||
// if ( this.pageId ) {
|
|
||||||
// this.pages.load(this.pageId).subscribe(pageRecord => {
|
|
||||||
// this.pageRecord = pageRecord;
|
|
||||||
// this.pages.get_nodes(pageRecord).subscribe((hosts: Array<HostRecord>) => {
|
|
||||||
// this.hostRecords = hosts;
|
|
||||||
// if ( !pageRecord.isViewOnly() ) {
|
|
||||||
// this.onSaveClick();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// this.router.navigate(['/home']);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onHostRecordChange($event, i) {
|
|
||||||
// if ( !this.pageRecord.isViewOnly() ) {
|
|
||||||
// this.hostRecords[i] = $event;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// async onAddClick($event) {
|
|
||||||
// if ( this.pageRecord.isViewOnly() ) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const popover = await this.popover.create({
|
|
||||||
// component: NodePickerComponent,
|
|
||||||
// event: $event,
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// popover.onDidDismiss().then(arg => {
|
|
||||||
// const defValue = this.getDefaultValue(arg.data);
|
|
||||||
// const hostRec = new HostRecord(defValue);
|
|
||||||
// hostRec.type = arg.data;
|
|
||||||
// hostRec.PageId = this.pageRecord.UUID;
|
|
||||||
//
|
|
||||||
// if ( hostRec.type === 'ul' ) {
|
|
||||||
// hostRec.value = JSON.stringify([{value: '', indentationLevel: 0}]);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.hostRecords.push(hostRec);
|
|
||||||
// if ( hostRec.isNorm() ) {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// this.editorHosts.toArray().reverse()[0].takeFocus();
|
|
||||||
// }, 0);
|
|
||||||
// } else {
|
|
||||||
// this.onSaveClick();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// await popover.present();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// getDefaultValue(type: string) {
|
|
||||||
// if ( type === 'paragraph' ) {
|
|
||||||
// return '';
|
|
||||||
// } else if ( type === 'header1' ) {
|
|
||||||
// return '# ';
|
|
||||||
// } else if ( type === 'header2' ) {
|
|
||||||
// return '## ';
|
|
||||||
// } else if ( type === 'header3' ) {
|
|
||||||
// return '### ';
|
|
||||||
// } else if ( type === 'header4' ) {
|
|
||||||
// return '#### ';
|
|
||||||
// } else if ( type === 'block_code' ) {
|
|
||||||
// return '```';
|
|
||||||
// } else if ( type === 'click_link' ) {
|
|
||||||
// return 'https://';
|
|
||||||
// } else if ( type === 'page_sep' ) {
|
|
||||||
// return '===';
|
|
||||||
// } else {
|
|
||||||
// return '';
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onNewHostRequested($event) {
|
|
||||||
// if ( this.pageRecord.isViewOnly() ) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const insertAfter = this.getIndexFromRecord($event.record);
|
|
||||||
// const record = new HostRecord('');
|
|
||||||
// const newHosts = []
|
|
||||||
// this.hostRecords.forEach((rec, i) => {
|
|
||||||
// newHosts.push(rec);
|
|
||||||
// if ( i === insertAfter ) {
|
|
||||||
// newHosts.push(record);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// this.hostRecords = newHosts;
|
|
||||||
//
|
|
||||||
// setTimeout(() => {
|
|
||||||
// this.editorHosts.toArray()[insertAfter + 1].takeFocus();
|
|
||||||
// }, 0);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onDestroyHostRequested($event) {
|
|
||||||
// if ( this.pageRecord.isViewOnly() ) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let removedIndex = 0;
|
|
||||||
// const newHostRecords = this.editorHosts.filter((host, i) => {
|
|
||||||
// if ( $event.record === host.record ) {
|
|
||||||
// removedIndex = i;
|
|
||||||
// }
|
|
||||||
// return host.record !== $event.record;
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// const removedHost = this.editorHosts[removedIndex];
|
|
||||||
//
|
|
||||||
// const hostRecords = newHostRecords.map(host => host.record);
|
|
||||||
// this.hostRecords = hostRecords;
|
|
||||||
//
|
|
||||||
// setTimeout(() => {
|
|
||||||
// let focusIndex;
|
|
||||||
// if ( removedIndex === 0 && this.editorHosts.toArray().length ) {
|
|
||||||
// focusIndex = 0;
|
|
||||||
// } else if ( removedIndex !== 0 ) {
|
|
||||||
// focusIndex = removedIndex - 1;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if ( focusIndex >= 0 ) {
|
|
||||||
// this.editorHosts.toArray()[focusIndex].takeFocus(false);
|
|
||||||
// }
|
|
||||||
// }, 0);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected getIndexFromRecord(record) {
|
|
||||||
// let index;
|
|
||||||
// this.editorHosts.toArray().forEach((host, i) => {
|
|
||||||
// if ( host.record === record ) {
|
|
||||||
// index = i;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// return index;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onSaveClick() {
|
|
||||||
// if ( this.pageRecord.isViewOnly() ) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.loader.create({message: 'Saving changes...'}).then(loader => {
|
|
||||||
// loader.present().then(() => {
|
|
||||||
// this.pageRecord.Name = this.titleBar.el.innerText.trim();
|
|
||||||
//
|
|
||||||
// // First, save the page record itself
|
|
||||||
// this.pages.save(this.pageRecord).subscribe(pageRecord => {
|
|
||||||
// this.pageRecord = pageRecord;
|
|
||||||
//
|
|
||||||
// // Now, save the nodes
|
|
||||||
// this.pages.save_nodes(pageRecord, this.hostRecords).subscribe(result => {
|
|
||||||
// this.hostRecords = result;
|
|
||||||
// loader.dismiss();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// async onOptionsClick($event, i) {
|
|
||||||
// if ( this.pageRecord.isViewOnly() ) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const popover = await this.popover.create({
|
|
||||||
// component: HostOptionsComponent,
|
|
||||||
// event: $event,
|
|
||||||
// componentProps: {
|
|
||||||
// editor: this,
|
|
||||||
// index: i,
|
|
||||||
// event: $event,
|
|
||||||
// hostRecord: this.hostRecords[i],
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// popover.onDidDismiss().then((result) => {
|
|
||||||
// if ( result.data === 'delete_node' ) {
|
|
||||||
// $event.record = this.hostRecords[i];
|
|
||||||
// this.onDestroyHostRequested($event);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// await popover.present();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onEditorKeydown($event) {
|
|
||||||
// if ( $event.key === 's' && $event.ctrlKey ) {
|
|
||||||
// $event.preventDefault();
|
|
||||||
// this.onSaveClick();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
150
src/app/service/db/Page.ts
Normal file
150
src/app/service/db/Page.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import {Model} from './Model';
|
||||||
|
|
||||||
|
export interface IPage {
|
||||||
|
id?: number;
|
||||||
|
UUID: string;
|
||||||
|
Name: string;
|
||||||
|
OrgUserId: string;
|
||||||
|
IsPublic: boolean;
|
||||||
|
IsVisibleInMenu: boolean;
|
||||||
|
ParentId: string;
|
||||||
|
NodeIds: string[];
|
||||||
|
CreatedAt: string;
|
||||||
|
UpdatedAt: string;
|
||||||
|
Active: boolean;
|
||||||
|
CreatedUserId: string;
|
||||||
|
UpdateUserId: string;
|
||||||
|
ChildPageIds: string[];
|
||||||
|
noDelete: boolean;
|
||||||
|
virtual: boolean;
|
||||||
|
needsServerUpdate?: boolean;
|
||||||
|
deleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Page extends Model<IPage> implements IPage {
|
||||||
|
id?: number;
|
||||||
|
UUID: string;
|
||||||
|
Name: string;
|
||||||
|
OrgUserId: string;
|
||||||
|
IsPublic: boolean;
|
||||||
|
IsVisibleInMenu: boolean;
|
||||||
|
ParentId: string;
|
||||||
|
NodeIds: string[];
|
||||||
|
CreatedAt: string;
|
||||||
|
UpdatedAt: string;
|
||||||
|
Active: boolean;
|
||||||
|
CreatedUserId: string;
|
||||||
|
UpdateUserId: string;
|
||||||
|
ChildPageIds: string[];
|
||||||
|
noDelete: boolean;
|
||||||
|
virtual: boolean;
|
||||||
|
needsServerUpdate?: boolean;
|
||||||
|
deleted?: boolean;
|
||||||
|
|
||||||
|
public static getTableName() {
|
||||||
|
return 'pages';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getSchema() {
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
return '++id, UUID, Name, OrgUserId, IsPublic, IsVisibleInMenu, ParentId, NodeIds, CreatedAt, UpdatedAt, Active, CreatedUserId, UpdateUserId, ChildPageIds, noDelete, virtual, needsServerUpdate, deleted';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
UUID: string,
|
||||||
|
Name: string,
|
||||||
|
OrgUserId: string,
|
||||||
|
IsPublic: boolean,
|
||||||
|
IsVisibleInMenu: boolean,
|
||||||
|
ParentId: string,
|
||||||
|
NodeIds: string[],
|
||||||
|
CreatedAt: string,
|
||||||
|
UpdatedAt: string,
|
||||||
|
Active: boolean,
|
||||||
|
CreatedUserId: string,
|
||||||
|
UpdateUserId: string,
|
||||||
|
ChildPageIds: string[],
|
||||||
|
noDelete: boolean,
|
||||||
|
virtual: boolean,
|
||||||
|
needsServerUpdate?: boolean,
|
||||||
|
deleted?: boolean,
|
||||||
|
id?: number
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.UUID = UUID;
|
||||||
|
this.Name = Name;
|
||||||
|
this.OrgUserId = OrgUserId;
|
||||||
|
this.IsPublic = IsPublic;
|
||||||
|
this.IsVisibleInMenu = IsVisibleInMenu;
|
||||||
|
this.ParentId = ParentId;
|
||||||
|
this.NodeIds = NodeIds;
|
||||||
|
this.CreatedAt = CreatedAt;
|
||||||
|
this.UpdatedAt = UpdatedAt;
|
||||||
|
this.Active = Active;
|
||||||
|
this.CreatedUserId = CreatedUserId;
|
||||||
|
this.UpdateUserId = UpdateUserId;
|
||||||
|
this.ChildPageIds = ChildPageIds;
|
||||||
|
this.noDelete = noDelete;
|
||||||
|
this.virtual = virtual;
|
||||||
|
|
||||||
|
if ( typeof needsServerUpdate !== 'undefined' ) {
|
||||||
|
this.needsServerUpdate = needsServerUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof deleted !== 'undefined' ) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( id ) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fillFromRecord(record: any) {
|
||||||
|
console.log('page fill from record', record);
|
||||||
|
this.UUID = record.UUID;
|
||||||
|
this.Name = record.Name;
|
||||||
|
this.OrgUserId = record.OrgUserId;
|
||||||
|
this.IsPublic = record.IsPublic;
|
||||||
|
this.IsVisibleInMenu = record.IsVisibleInMenu;
|
||||||
|
this.ParentId = record.ParentId;
|
||||||
|
this.NodeIds = record.NodeIds;
|
||||||
|
console.log('setting node ids', this.NodeIds, record.NodeIds);
|
||||||
|
this.CreatedAt = String(record.CreatedAt);
|
||||||
|
this.UpdatedAt = String(record.UpdatedAt);
|
||||||
|
this.Active = record.Active;
|
||||||
|
this.CreatedUserId = record.CreatedUserId;
|
||||||
|
this.UpdateUserId = record.UpdateUserId;
|
||||||
|
this.ChildPageIds = record.ChildPageIds;
|
||||||
|
this.noDelete = record.noDelete;
|
||||||
|
this.virtual = record.virtual;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSaveRecord(): any {
|
||||||
|
return {
|
||||||
|
...(this.id ? { id: this.id } : {}),
|
||||||
|
UUID: this.UUID,
|
||||||
|
Name: this.Name,
|
||||||
|
OrgUserId: this.OrgUserId,
|
||||||
|
IsPublic: this.IsPublic,
|
||||||
|
IsVisibleInMenu: this.IsVisibleInMenu,
|
||||||
|
ParentId: this.ParentId,
|
||||||
|
NodeIds: this.NodeIds,
|
||||||
|
CreatedAt: new Date(this.CreatedAt),
|
||||||
|
UpdatedAt: new Date(this.UpdatedAt),
|
||||||
|
Active: this.Active,
|
||||||
|
CreatedUserId: this.CreatedUserId,
|
||||||
|
UpdateUserId: this.UpdateUserId,
|
||||||
|
ChildPageIds: this.ChildPageIds,
|
||||||
|
noDelete: this.noDelete,
|
||||||
|
virtual: this.virtual,
|
||||||
|
...(typeof this.needsServerUpdate === 'undefined' ? {} : { needsServerUpdate: this.needsServerUpdate }),
|
||||||
|
...(typeof this.deleted === 'undefined' ? {} : { deleted: this.deleted }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDatabase(): Dexie.Table<IPage, number> {
|
||||||
|
return this.staticClass().dbService.table('pages') as Dexie.Table<IPage, number>;
|
||||||
|
}
|
||||||
|
}
|
113
src/app/service/db/PageNode.ts
Normal file
113
src/app/service/db/PageNode.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import {Model} from './Model';
|
||||||
|
|
||||||
|
export interface IPageNode {
|
||||||
|
id?: number;
|
||||||
|
UUID: string;
|
||||||
|
Type: string;
|
||||||
|
ValueJSON: string;
|
||||||
|
PageId: string;
|
||||||
|
CreatedAt: string;
|
||||||
|
UpdatedAt: string;
|
||||||
|
CreatedUserId: string;
|
||||||
|
UpdateUserId: string;
|
||||||
|
needsServerUpdate?: boolean;
|
||||||
|
deleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PageNode extends Model<IPageNode> implements IPageNode {
|
||||||
|
id?: number;
|
||||||
|
UUID: string;
|
||||||
|
Type: string;
|
||||||
|
ValueJSON: string;
|
||||||
|
PageId: string;
|
||||||
|
CreatedAt: string;
|
||||||
|
UpdatedAt: string;
|
||||||
|
CreatedUserId: string;
|
||||||
|
UpdateUserId: string;
|
||||||
|
needsServerUpdate?: boolean;
|
||||||
|
deleted?: boolean;
|
||||||
|
|
||||||
|
public static getTableName() {
|
||||||
|
return 'pageNodes';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getSchema() {
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
return '++id, UUID, Type, ValueJSON, PageId, CreatedAt, UpdatedAt, CreatedUserId, UpdateUserId, needsServerUpdate, deleted';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
UUID: string,
|
||||||
|
Type: string,
|
||||||
|
ValueJSON: string,
|
||||||
|
PageId: string,
|
||||||
|
CreatedAt: string,
|
||||||
|
UpdatedAt: string,
|
||||||
|
CreatedUserId: string,
|
||||||
|
UpdateUserId: string,
|
||||||
|
needsServerUpdate?: boolean,
|
||||||
|
deleted?: boolean,
|
||||||
|
id?: number
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.UUID = UUID;
|
||||||
|
this.Type = Type;
|
||||||
|
this.ValueJSON = ValueJSON;
|
||||||
|
this.PageId = PageId;
|
||||||
|
this.CreatedAt = CreatedAt;
|
||||||
|
this.UpdatedAt = UpdatedAt;
|
||||||
|
this.CreatedUserId = CreatedUserId;
|
||||||
|
this.UpdateUserId = UpdateUserId;
|
||||||
|
|
||||||
|
if ( typeof needsServerUpdate !== 'undefined' ) {
|
||||||
|
this.needsServerUpdate = needsServerUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof deleted !== 'undefined' ) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( id ) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fillFromRecord(record: any) {
|
||||||
|
this.UUID = record.UUID;
|
||||||
|
this.Type = record.Type;
|
||||||
|
this.ValueJSON = JSON.stringify(record.Value);
|
||||||
|
this.PageId = record.PageId;
|
||||||
|
this.CreatedAt = String(record.CreatedAt);
|
||||||
|
this.UpdatedAt = String(record.UpdatedAt);
|
||||||
|
this.CreatedUserId = record.CreatedUserId;
|
||||||
|
this.UpdateUserId = record.UpdateUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inflateToRecord() {
|
||||||
|
const record = this.getSaveRecord();
|
||||||
|
record.Value = JSON.parse(record.ValueJSON);
|
||||||
|
delete record.ValueJSON;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSaveRecord(): any {
|
||||||
|
return {
|
||||||
|
...(this.id ? { id: this.id } : {}),
|
||||||
|
UUID: this.UUID,
|
||||||
|
Type: this.Type,
|
||||||
|
ValueJSON: this.ValueJSON,
|
||||||
|
PageId: this.PageId,
|
||||||
|
CreatedAt: new Date(this.CreatedAt),
|
||||||
|
UpdatedAt: new Date(this.UpdatedAt),
|
||||||
|
CreatedUserId: this.CreatedUserId,
|
||||||
|
UpdateUserId: this.UpdateUserId,
|
||||||
|
...(typeof this.needsServerUpdate === 'undefined' ? {} : { needsServerUpdate: this.needsServerUpdate }),
|
||||||
|
...(typeof this.deleted === 'undefined' ? {} : { deleted: this.deleted }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDatabase(): Dexie.Table<IPageNode, number> {
|
||||||
|
return this.staticClass().dbService.table('pageNodes') as Dexie.Table<IPageNode, number>;
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,17 @@ import {Database, IDatabase} from './Database';
|
|||||||
import {DatabaseColumn, IDatabaseColumn} from './DatabaseColumn';
|
import {DatabaseColumn, IDatabaseColumn} from './DatabaseColumn';
|
||||||
import {DatabaseEntry, IDatabaseEntry} from './DatabaseEntry';
|
import {DatabaseEntry, IDatabaseEntry} from './DatabaseEntry';
|
||||||
import {FileGroup, IFileGroup} from './FileGroup';
|
import {FileGroup, IFileGroup} from './FileGroup';
|
||||||
|
import {Page, IPage} from './Page';
|
||||||
|
import {PageNode, IPageNode} from './PageNode';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DatabaseService extends Dexie {
|
export class DatabaseService extends Dexie {
|
||||||
protected static registeredModels = [Migration, MenuItem, KeyValue, Codium, Database, DatabaseColumn, DatabaseEntry, FileGroup];
|
protected static registeredModels = [
|
||||||
|
Migration, MenuItem, KeyValue, Codium, Database, DatabaseColumn, DatabaseEntry, FileGroup, Page, PageNode
|
||||||
|
];
|
||||||
|
|
||||||
protected initialized = false;
|
protected initialized = false;
|
||||||
|
|
||||||
migrations!: Dexie.Table<IMigration, number>;
|
migrations!: Dexie.Table<IMigration, number>;
|
||||||
@ -24,6 +29,8 @@ export class DatabaseService extends Dexie {
|
|||||||
databaseColumns!: Dexie.Table<IDatabaseColumn, number>;
|
databaseColumns!: Dexie.Table<IDatabaseColumn, number>;
|
||||||
databaseEntries!: Dexie.Table<IDatabaseEntry, number>;
|
databaseEntries!: Dexie.Table<IDatabaseEntry, number>;
|
||||||
fileGroups!: Dexie.Table<IFileGroup, number>;
|
fileGroups!: Dexie.Table<IFileGroup, number>;
|
||||||
|
pages!: Dexie.Table<IPage, number>;
|
||||||
|
pageNodes!: Dexie.Table<IPageNode, number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
) {
|
) {
|
||||||
@ -56,7 +63,7 @@ export class DatabaseService extends Dexie {
|
|||||||
schema[ModelClass.getTableName()] = ModelClass.getSchema();
|
schema[ModelClass.getTableName()] = ModelClass.getSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.version(11).stores(schema);
|
await this.version(14).stores(schema);
|
||||||
await this.open();
|
await this.open();
|
||||||
|
|
||||||
this.migrations = this.table('migrations');
|
this.migrations = this.table('migrations');
|
||||||
@ -82,6 +89,12 @@ export class DatabaseService extends Dexie {
|
|||||||
|
|
||||||
this.fileGroups = this.table('fileGroups');
|
this.fileGroups = this.table('fileGroups');
|
||||||
this.fileGroups.mapToClass(FileGroup);
|
this.fileGroups.mapToClass(FileGroup);
|
||||||
|
|
||||||
|
this.pages = this.table('pages');
|
||||||
|
this.pages.mapToClass(Page);
|
||||||
|
|
||||||
|
this.pageNodes = this.table('pageNodes');
|
||||||
|
this.pageNodes.mapToClass(PageNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async purge() {
|
public async purge() {
|
||||||
@ -96,6 +109,8 @@ export class DatabaseService extends Dexie {
|
|||||||
this.databaseColumns.clear(),
|
this.databaseColumns.clear(),
|
||||||
this.databaseEntries.clear(),
|
this.databaseEntries.clear(),
|
||||||
this.fileGroups.clear(),
|
this.fileGroups.clear(),
|
||||||
|
this.pages.clear(),
|
||||||
|
this.pageNodes.clear(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {ApiService} from './api.service';
|
import {ApiService, ResourceNotAvailableOfflineError} from './api.service';
|
||||||
import PageRecord from '../structures/PageRecord';
|
import PageRecord from '../structures/PageRecord';
|
||||||
import HostRecord from '../structures/HostRecord';
|
import HostRecord from '../structures/HostRecord';
|
||||||
import {EditorNodeContract} from '../components/nodes/EditorNode.contract';
|
import {EditorNodeContract} from '../components/nodes/EditorNode.contract';
|
||||||
import {BehaviorSubject, Subscription} from 'rxjs';
|
import {BehaviorSubject, Subscription} from 'rxjs';
|
||||||
import {NavigationService} from './navigation.service';
|
import {NavigationService} from './navigation.service';
|
||||||
|
import {DatabaseService} from './db/database.service';
|
||||||
|
import {Page} from './db/Page';
|
||||||
|
import {PageNode} from './db/PageNode';
|
||||||
|
|
||||||
export class NoPageLoadedError extends Error {
|
export class NoPageLoadedError extends Error {
|
||||||
constructor(msg = 'There is no page open for editing.') {
|
constructor(msg = 'There is no page open for editing.') {
|
||||||
@ -35,6 +38,8 @@ export class EditorService {
|
|||||||
protected subs: Subscription[] = [];
|
protected subs: Subscription[] = [];
|
||||||
protected saving = false;
|
protected saving = false;
|
||||||
protected saveTriggered = false;
|
protected saveTriggered = false;
|
||||||
|
public notAvailable = false;
|
||||||
|
|
||||||
protected privTriggerSave = debounce(() => {
|
protected privTriggerSave = debounce(() => {
|
||||||
if ( this.saving ) {
|
if ( this.saving ) {
|
||||||
this.triggerSave();
|
this.triggerSave();
|
||||||
@ -84,6 +89,7 @@ export class EditorService {
|
|||||||
constructor(
|
constructor(
|
||||||
protected api: ApiService,
|
protected api: ApiService,
|
||||||
protected nav: NavigationService,
|
protected nav: NavigationService,
|
||||||
|
protected db: DatabaseService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async startEditing(pageId: string) {
|
async startEditing(pageId: string) {
|
||||||
@ -91,9 +97,19 @@ export class EditorService {
|
|||||||
await this.stopEditing();
|
await this.stopEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
this.currentPage = await this.loadPage(pageId);
|
this.currentPage = await this.loadPage(pageId);
|
||||||
this.currentNodes = await this.loadNodes(pageId);
|
this.currentNodes = await this.loadNodes(pageId);
|
||||||
|
this.notAvailable = false;
|
||||||
await this.ready$.next(true);
|
await this.ready$.next(true);
|
||||||
|
} catch (e) {
|
||||||
|
if ( e instanceof ResourceNotAvailableOfflineError ) {
|
||||||
|
this.notAvailable = true;
|
||||||
|
await this.ready$.next(true);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopEditing() {
|
async stopEditing() {
|
||||||
@ -159,12 +175,70 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async savePage(page: PageRecord): Promise<void> {
|
async savePage(page: PageRecord): Promise<void> {
|
||||||
await new Promise((res, rej) => {
|
await new Promise(async (res, rej) => {
|
||||||
|
const existingLocalPage = await this.db.pages.where({ UUID: page.UUID }).first() as Page;
|
||||||
const saveData = page.toSave();
|
const saveData = page.toSave();
|
||||||
|
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
if ( existingLocalPage ) {
|
||||||
|
existingLocalPage.fillFromRecord(page);
|
||||||
|
existingLocalPage.UpdatedAt = String(new Date()); // FIXME update UpdateUserId
|
||||||
|
existingLocalPage.needsServerUpdate = true;
|
||||||
|
await existingLocalPage.save();
|
||||||
|
} else {
|
||||||
|
const newLocalPage = new Page(
|
||||||
|
page.UUID,
|
||||||
|
page.Name,
|
||||||
|
page.OrgUserId,
|
||||||
|
page.IsPublic,
|
||||||
|
page.IsVisibleInMenu,
|
||||||
|
page.ParentId,
|
||||||
|
page.NodeIds,
|
||||||
|
String(page.CreatedAt || new Date()),
|
||||||
|
String(new Date()),
|
||||||
|
true,
|
||||||
|
page.CreatedUserId,
|
||||||
|
page.UpdateUserId, // FIXME fill in the current user's ID
|
||||||
|
page.ChildPageIds,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalPage.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res();
|
||||||
|
}
|
||||||
|
|
||||||
this.api.post(`/page/${page.UUID}/save`, saveData).subscribe({
|
this.api.post(`/page/${page.UUID}/save`, saveData).subscribe({
|
||||||
next: result => {
|
next: async result => {
|
||||||
console.log('save result', result);
|
if ( existingLocalPage ) {
|
||||||
|
existingLocalPage.fillFromRecord(page);
|
||||||
|
await existingLocalPage.save();
|
||||||
|
} else {
|
||||||
|
const newLocalPage = new Page(
|
||||||
|
result.data.UUID,
|
||||||
|
result.data.Name,
|
||||||
|
result.data.OrgUserId,
|
||||||
|
result.data.IsPublic,
|
||||||
|
result.data.IsVisibleInMenu,
|
||||||
|
result.data.ParentId,
|
||||||
|
result.data.NodeIds,
|
||||||
|
String(result.data.CreatedAt),
|
||||||
|
String(result.data.UpdatedAt),
|
||||||
|
true,
|
||||||
|
result.data.CreatedUserId,
|
||||||
|
result.data.UpdateUserId,
|
||||||
|
result.data.ChildPageIds,
|
||||||
|
result.data.noDelete,
|
||||||
|
result.data.virtual,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalPage.save();
|
||||||
|
}
|
||||||
|
|
||||||
res();
|
res();
|
||||||
},
|
},
|
||||||
error: rej,
|
error: rej,
|
||||||
@ -173,19 +247,85 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveNodesAsPage(page: PageRecord, nodes: HostRecord[]): Promise<HostRecord[]> {
|
async saveNodesAsPage(page: PageRecord, nodes: HostRecord[]): Promise<HostRecord[]> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
const saveNodes = nodes.map(x => {
|
const saveNodes = nodes.map(x => {
|
||||||
x.PageId = page.UUID;
|
x.PageId = page.UUID;
|
||||||
return x.toSave();
|
return x.toSave();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.api.post(`/page/${page.UUID}/nodes/save`, saveNodes).subscribe({
|
const existingLocalPage = await this.db.pages.where({ UUID: page.UUID }).first() as Page;
|
||||||
next: result => {
|
|
||||||
res(result.data.map(rec => {
|
// If we're offline save the nodes locally
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
if ( !existingLocalPage ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeRecs: PageNode[] = [];
|
||||||
|
for ( const nodeRec of saveNodes ) {
|
||||||
|
const existingLocalNode = await this.db.pageNodes.where({ UUID: nodeRec.UUID }).first() as PageNode;
|
||||||
|
if ( existingLocalNode ) {
|
||||||
|
existingLocalNode.fillFromRecord(nodeRec);
|
||||||
|
existingLocalNode.needsServerUpdate = true;
|
||||||
|
await existingLocalNode.save();
|
||||||
|
nodeRecs.push(existingLocalNode);
|
||||||
|
} else {
|
||||||
|
const newLocalNode = new PageNode(
|
||||||
|
nodeRec.UUID || PageNode.getUUID(),
|
||||||
|
nodeRec.Type,
|
||||||
|
JSON.stringify(nodeRec.Value),
|
||||||
|
nodeRec.PageId,
|
||||||
|
nodeRec.CreatedAt,
|
||||||
|
nodeRec.UpdatedAt,
|
||||||
|
nodeRec.CreatedUserId,
|
||||||
|
nodeRec.UpdateUserId,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalNode.save();
|
||||||
|
nodeRecs.push(newLocalNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page.NodeIds = nodeRecs.map(x => x.UUID);
|
||||||
|
existingLocalPage.NodeIds = nodeRecs.map(x => x.UUID);
|
||||||
|
existingLocalPage.needsServerUpdate = true;
|
||||||
|
await existingLocalPage.save();
|
||||||
|
|
||||||
|
return res(nodeRecs.map(x => {
|
||||||
|
const rec = x.inflateToRecord();
|
||||||
const host = new HostRecord(rec.Value.Value);
|
const host = new HostRecord(rec.Value.Value);
|
||||||
host.load(rec);
|
host.load(rec);
|
||||||
return host;
|
return host;
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, use the server to save them and update the local records
|
||||||
|
this.api.post(`/page/${page.UUID}/nodes/save`, saveNodes).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
await this.db.pageNodes.where({ PageId: page.UUID }).delete();
|
||||||
|
|
||||||
|
const returns = [];
|
||||||
|
for ( const rec of result.data ) {
|
||||||
|
const newLocalNode = new PageNode(
|
||||||
|
rec.UUID,
|
||||||
|
rec.Type,
|
||||||
|
JSON.stringify(rec.Value),
|
||||||
|
rec.PageId,
|
||||||
|
rec.CreatedAt,
|
||||||
|
rec.UpdatedAt,
|
||||||
|
rec.CreatedUserId,
|
||||||
|
rec.UpdateUsetId,
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalNode.save();
|
||||||
|
|
||||||
|
const host = new HostRecord(rec.Value.Value);
|
||||||
|
host.load(rec);
|
||||||
|
returns.push(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res(returns);
|
||||||
},
|
},
|
||||||
error: rej,
|
error: rej,
|
||||||
});
|
});
|
||||||
@ -193,12 +333,60 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveNodeToPage(page: PageRecord, node: HostRecord): Promise<HostRecord> {
|
async saveNodeToPage(page: PageRecord, node: HostRecord): Promise<HostRecord> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
node.PageId = page.UUID;
|
node.PageId = page.UUID;
|
||||||
const nodeData = node.toSave();
|
const nodeData = node.toSave();
|
||||||
|
const localPage = await this.db.pages.where({ UUID: page.UUID }).first() as Page;
|
||||||
|
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
if ( !localPage ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
const newLocalNode = new PageNode(
|
||||||
|
nodeData.UUID || PageNode.getUUID(),
|
||||||
|
nodeData.Type,
|
||||||
|
JSON.stringify(nodeData.Value),
|
||||||
|
nodeData.PageId,
|
||||||
|
nodeData.CreatedAt,
|
||||||
|
nodeData.UpdatedAt,
|
||||||
|
nodeData.CreatedUserId,
|
||||||
|
nodeData.UpdateUserId,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalNode.save();
|
||||||
|
|
||||||
|
localPage.NodeIds.push(newLocalNode.UUID);
|
||||||
|
localPage.needsServerUpdate = true;
|
||||||
|
await localPage.save();
|
||||||
|
|
||||||
|
const host = new HostRecord(nodeData.Value.Value);
|
||||||
|
host.load(nodeData);
|
||||||
|
return res(host);
|
||||||
|
}
|
||||||
|
|
||||||
this.api.post(`/page/${page.UUID}/nodes/save_one`, { nodeData }).subscribe({
|
this.api.post(`/page/${page.UUID}/nodes/save_one`, { nodeData }).subscribe({
|
||||||
next: result => {
|
next: async result => {
|
||||||
|
const newLocalNode = new PageNode(
|
||||||
|
result.data.UUID,
|
||||||
|
result.data.Type,
|
||||||
|
JSON.stringify(result.data.Value),
|
||||||
|
result.data.PageId,
|
||||||
|
result.data.CreatedAt,
|
||||||
|
result.data.UpdatedAt,
|
||||||
|
result.data.CreatedUserId,
|
||||||
|
result.data.UpdateUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalNode.save();
|
||||||
|
|
||||||
|
if ( localPage ) {
|
||||||
|
localPage.NodeIds.push(result.data.UUID);
|
||||||
|
console.log('saving local page data', result.data.UUID);
|
||||||
|
await localPage.save();
|
||||||
|
}
|
||||||
|
|
||||||
const host = new HostRecord(result.data.Value.Value);
|
const host = new HostRecord(result.data.Value.Value);
|
||||||
host.load(result.data);
|
host.load(result.data);
|
||||||
res(host);
|
res(host);
|
||||||
@ -235,6 +423,16 @@ export class EditorService {
|
|||||||
delete this.nodeIdToEditorContract[nodeId];
|
delete this.nodeIdToEditorContract[nodeId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're offline, we need to flag the local node record for deletion
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
const existingLocalNode = await this.db.pageNodes.where({ UUID: nodeId }).first() as PageNode;
|
||||||
|
if ( existingLocalNode ) {
|
||||||
|
existingLocalNode.deleted = true;
|
||||||
|
existingLocalNode.needsServerUpdate = true;
|
||||||
|
await existingLocalNode.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.currentNodes = this.currentNodes.filter(x => x.UUID !== nodeId);
|
this.currentNodes = this.currentNodes.filter(x => x.UUID !== nodeId);
|
||||||
this.dirtyOverride = true;
|
this.dirtyOverride = true;
|
||||||
this.triggerSave();
|
this.triggerSave();
|
||||||
@ -270,6 +468,7 @@ export class EditorService {
|
|||||||
this.currentNodes.push(host);
|
this.currentNodes.push(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.currentPage.NodeIds.push(host.UUID);
|
||||||
this.dirtyOverride = true;
|
this.dirtyOverride = true;
|
||||||
this.triggerSave();
|
this.triggerSave();
|
||||||
return host;
|
return host;
|
||||||
@ -325,10 +524,50 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadPage(pageId: string): Promise<PageRecord> {
|
async loadPage(pageId: string): Promise<PageRecord> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
|
const existingLocalPage = await this.db.pages.where({ UUID: pageId }).first() as Page;
|
||||||
|
|
||||||
|
// If we're offline, return the local record, or throw an error.
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
if ( existingLocalPage ) {
|
||||||
|
return res(existingLocalPage.getSaveRecord());
|
||||||
|
} else {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're online, fetch the page record and store it locally
|
||||||
this.api.get(`/page/${pageId}`).subscribe({
|
this.api.get(`/page/${pageId}`).subscribe({
|
||||||
next: result => {
|
next: async result => {
|
||||||
res(new PageRecord(result.data));
|
const page = new PageRecord(result.data);
|
||||||
|
|
||||||
|
if ( existingLocalPage ) {
|
||||||
|
existingLocalPage.fillFromRecord(result.data);
|
||||||
|
existingLocalPage.needsServerUpdate = false;
|
||||||
|
await existingLocalPage.save();
|
||||||
|
} else {
|
||||||
|
const newLocalPage = new Page(
|
||||||
|
page.UUID,
|
||||||
|
page.Name,
|
||||||
|
page.OrgUserId,
|
||||||
|
page.IsPublic,
|
||||||
|
page.IsVisibleInMenu,
|
||||||
|
page.ParentId,
|
||||||
|
page.NodeIds,
|
||||||
|
String(page.CreatedAt),
|
||||||
|
String(page.UpdatedAt),
|
||||||
|
true,
|
||||||
|
page.CreatedUserId,
|
||||||
|
page.UpdateUserId,
|
||||||
|
page.ChildPageIds,
|
||||||
|
result.data.noDelete,
|
||||||
|
result.data.virtual,
|
||||||
|
);
|
||||||
|
|
||||||
|
await newLocalPage.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
res(page);
|
||||||
},
|
},
|
||||||
error: rej,
|
error: rej,
|
||||||
});
|
});
|
||||||
@ -336,14 +575,44 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadNodes(pageId: string): Promise<HostRecord[]> {
|
async loadNodes(pageId: string): Promise<HostRecord[]> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
this.api.get(`/page/${pageId}/nodes`).subscribe({
|
const existingNodes = await this.db.pageNodes.where({ PageId: pageId }).toArray() as PageNode[];
|
||||||
next: result => {
|
|
||||||
res(result.data.map(rec => {
|
const inflateRecords = (records) => {
|
||||||
|
return records.map(rec => {
|
||||||
const host = new HostRecord(rec.Value.Value);
|
const host = new HostRecord(rec.Value.Value);
|
||||||
host.load(rec);
|
host.load(rec);
|
||||||
return host;
|
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`).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,
|
error: rej,
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user