Cache pages and page nodes offline
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Garrett Mills 2020-10-28 08:49:50 -05:00
parent 8f5ad697b3
commit 6bcb4bf455
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
5 changed files with 129 additions and 112 deletions

View File

@ -22,6 +22,7 @@ import {SearchComponent} from './components/search/Search.component';
import {NodeTypeIcons} from './structures/node-types'; import {NodeTypeIcons} from './structures/node-types';
import {NavigationService} from './service/navigation.service'; import {NavigationService} from './service/navigation.service';
import {DatabaseService} from './service/db/database.service'; import {DatabaseService} from './service/db/database.service';
import {EditorService} from './service/editor.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -97,6 +98,7 @@ export class AppComponent implements OnInit {
protected navService: NavigationService, protected navService: NavigationService,
protected toasts: ToastController, protected toasts: ToastController,
protected db: DatabaseService, protected db: DatabaseService,
protected editor: EditorService,
) { ) {
this.initializeApp(); this.initializeApp();
} }
@ -303,7 +305,7 @@ export class AppComponent implements OnInit {
{ {
text: 'Create', text: 'Create',
handler: async args => { handler: async args => {
const page = await this.api.createPage(args.name); const page = await this.editor.createPage(args.name);
this.reloadMenuItems().subscribe(); this.reloadMenuItems().subscribe();
await this.router.navigate(['/editor', { id: page.UUID }]); await this.router.navigate(['/editor', { id: page.UUID }]);
} }

View File

@ -870,101 +870,4 @@ export class ApiService {
}); });
}); });
} }
public createPage(name: string): Promise<any> {
return new Promise(async (res, rej) => {
// If we're offline, create a stub page to be saved later
if ( this.isOffline ) {
const page = new Page(
Page.getUUID(),
'New Page',
'OrgUserId',
true,
true,
'0',
[],
String(new Date()),
String(new Date()),
true,
'create user id',
'update user id',
[],
false,
false,
true
);
const firstNode = new PageNode(
PageNode.getUUID(),
'paragraph',
JSON.stringify({ Value: 'Click to edit...' }),
page.UUID,
String(new Date()),
String(new Date()),
'create user id',
'update user id',
true
);
await firstNode.save();
page.NodeIds.push(firstNode.UUID);
await page.save();
// Because we're offline, we need to manually create the menu item node
const topLevelItem = await this.db.menuItems.where({
serverId: 0,
name: 'My Info Tree',
}).first() as MenuItem;
if ( topLevelItem ) {
const newItem = new MenuItem(
page.Name,
MenuItem.getUUID(),
[],
false,
false,
false,
'page',
false,
true
);
await newItem.save();
topLevelItem.childIds.push(newItem.serverId);
await topLevelItem.save();
}
return res(page.getSaveRecord());
}
// If we're online, the server will handle all of that mess...
this.post('/page/create', { name }).subscribe({
next: async result => {
const page = new Page(
result.data.UUID,
result.data.Name,
result.data.OrgUserId,
result.data.IsPublic,
result.data.IsVisibleInMenu,
result.data.ParentId,
result.data.NodeIds,
result.data.CreatedAt,
result.data.UpdatedAt,
true,
result.data.CreatedUserId,
result.data.UpdateUserId,
result.data.ChildPageIds,
result.data.noDelete,
result.data.virtual
);
await page.save();
return res(result.data);
},
error: rej,
});
});
}
} }

View File

@ -8,6 +8,9 @@ import {NavigationService} from './navigation.service';
import {DatabaseService} from './db/database.service'; import {DatabaseService} from './db/database.service';
import {Page} from './db/Page'; import {Page} from './db/Page';
import {PageNode} from './db/PageNode'; import {PageNode} from './db/PageNode';
import {MenuItem} from './db/MenuItem';
import {SessionService} from './session.service';
import {debounce} from '../utility';
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.') {
@ -15,17 +18,6 @@ export class NoPageLoadedError extends Error {
} }
} }
export function debounce(func: (...args: any[]) => any, timeout?: number) {
let timer: number | undefined;
return (...args: any[]) => {
const next = () => func(...args);
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(next, timeout > 0 ? timeout : 300);
};
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
@ -90,6 +82,7 @@ export class EditorService {
protected api: ApiService, protected api: ApiService,
protected nav: NavigationService, protected nav: NavigationService,
protected db: DatabaseService, protected db: DatabaseService,
protected session: SessionService,
) { } ) { }
async startEditing(pageId: string) { async startEditing(pageId: string) {
@ -175,6 +168,7 @@ export class EditorService {
} }
async savePage(page: PageRecord): Promise<void> { async savePage(page: PageRecord): Promise<void> {
console.log('saving page', page);
await new Promise(async (res, rej) => { await new Promise(async (res, rej) => {
const existingLocalPage = await this.db.pages.where({ UUID: page.UUID }).first() as Page; const existingLocalPage = await this.db.pages.where({ UUID: page.UUID }).first() as Page;
const saveData = page.toSave(); const saveData = page.toSave();
@ -343,8 +337,12 @@ export class EditorService {
return rej(new ResourceNotAvailableOfflineError()); return rej(new ResourceNotAvailableOfflineError());
} }
if ( !nodeData.UUID ) {
nodeData.UUID = PageNode.getUUID();
}
const newLocalNode = new PageNode( const newLocalNode = new PageNode(
nodeData.UUID || PageNode.getUUID(), nodeData.UUID,
nodeData.Type, nodeData.Type,
JSON.stringify(nodeData.Value), JSON.stringify(nodeData.Value),
nodeData.PageId, nodeData.PageId,
@ -448,6 +446,7 @@ export class EditorService {
baseHost.PageId = this.currentPage.UUID; baseHost.PageId = this.currentPage.UUID;
const host = await this.saveNodeToPage(this.currentPage, baseHost); const host = await this.saveNodeToPage(this.currentPage, baseHost);
console.log('added node to page', host)
let placed = false; let placed = false;
if ( position === 'before' && positionNodeId ) { if ( position === 'before' && positionNodeId ) {
@ -530,7 +529,7 @@ export class EditorService {
// If we're offline, return the local record, or throw an error. // If we're offline, return the local record, or throw an error.
if ( this.api.isOffline ) { if ( this.api.isOffline ) {
if ( existingLocalPage ) { if ( existingLocalPage ) {
return res(existingLocalPage.getSaveRecord()); return res(new PageRecord(existingLocalPage.getSaveRecord()));
} else { } else {
return rej(new ResourceNotAvailableOfflineError()); return rej(new ResourceNotAvailableOfflineError());
} }
@ -618,4 +617,106 @@ export class EditorService {
}); });
}); });
} }
public createPage(name: string): Promise<any> {
return new Promise(async (res, rej) => {
const userId = this.session.get('user.id');
if ( !userId ) {
throw new ResourceNotAvailableOfflineError();
}
// If we're offline, create a stub page to be saved later
if ( this.api.isOffline ) {
const page = new Page(
Page.getUUID(),
name,
userId,
true,
true,
'0',
[],
String(new Date()),
String(new Date()),
true,
userId,
userId,
[],
false,
false,
true
);
const firstNode = new PageNode(
PageNode.getUUID(),
'paragraph',
JSON.stringify({ Value: 'Click to edit...' }),
page.UUID,
String(new Date()),
String(new Date()),
userId,
userId,
true
);
await firstNode.save();
page.NodeIds.push(firstNode.UUID);
await page.save();
// Because we're offline, we need to manually create the menu item node
const topLevelItem = await this.db.menuItems.where({
serverId: 0,
name: 'My Info Tree',
}).first() as MenuItem;
if ( topLevelItem ) {
const newItem = new MenuItem(
page.Name,
page.UUID,
[],
false,
false,
false,
'page',
false,
true
);
await newItem.save();
topLevelItem.childIds.push(newItem.serverId);
await topLevelItem.save();
}
return res(page.getSaveRecord());
}
// If we're online, the server will handle all of that mess...
this.api.post('/page/create', { name }).subscribe({
next: async result => {
const page = new Page(
result.data.UUID,
result.data.Name,
result.data.OrgUserId,
result.data.IsPublic,
result.data.IsVisibleInMenu,
result.data.ParentId,
result.data.NodeIds,
result.data.CreatedAt,
result.data.UpdatedAt,
true,
result.data.CreatedUserId,
result.data.UpdateUserId,
result.data.ChildPageIds,
result.data.noDelete,
result.data.virtual
);
await page.save();
return res(result.data);
},
error: rej,
});
});
}
} }

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import {ApiService} from './api.service'; import {ApiService} from './api.service';
import {debounce} from './editor.service'; import {debounce} from '../utility';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

View File

@ -6,3 +6,14 @@ export function uuid_v4() {
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
); );
} }
export function debounce(func: (...args: any[]) => any, timeout?: number) {
let timer: number | undefined;
return (...args: any[]) => {
const next = () => func(...args);
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(next, timeout > 0 ? timeout : 300);
};
}