Add ability to prefetch and auto-prefetch offline data
This commit is contained in:
parent
44026f1306
commit
f788654ff7
@ -168,6 +168,9 @@ export class AppComponent implements OnInit {
|
|||||||
toggleDark: () => this.toggleDark(),
|
toggleDark: () => this.toggleDark(),
|
||||||
isDark: () => this.isDark(),
|
isDark: () => this.isDark(),
|
||||||
showSearch: () => this.handleKeyboardEvent(),
|
showSearch: () => this.handleKeyboardEvent(),
|
||||||
|
isPrefetch: () => this.isPrefetch(),
|
||||||
|
togglePrefetch: () => this.togglePrefetch(),
|
||||||
|
doPrefetch: () => this.doPrefetch(),
|
||||||
}
|
}
|
||||||
}).then(popover => popover.present());
|
}).then(popover => popover.present());
|
||||||
}
|
}
|
||||||
@ -472,6 +475,42 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
await this.statusBar.styleDefault();
|
await this.statusBar.styleDefault();
|
||||||
await this.splashScreen.hide();
|
await this.splashScreen.hide();
|
||||||
|
|
||||||
|
// If we went online after being offline, sync the local data
|
||||||
|
if ( !this.api.isOffline && await this.api.needsSync() ) {
|
||||||
|
this.loader.message = 'Syncing data...';
|
||||||
|
try {
|
||||||
|
await this.api.syncOfflineData();
|
||||||
|
} catch (e) {
|
||||||
|
this.toasts.create({
|
||||||
|
cssClass: 'compat-toast-container',
|
||||||
|
message: 'An error occurred while syncing offline data. Not all data was saved.',
|
||||||
|
buttons: [
|
||||||
|
'Okay'
|
||||||
|
],
|
||||||
|
}).then(tst => {
|
||||||
|
tst.present();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.isPrefetch() ) {
|
||||||
|
this.loader.message = 'Downloading data...'; // TODO actually do the prefetch
|
||||||
|
try {
|
||||||
|
await this.api.prefetchOfflineData();
|
||||||
|
} catch (e) {
|
||||||
|
this.toasts.create({
|
||||||
|
cssClass: 'compat-toast-container',
|
||||||
|
message: 'An error occurred while pre-fetching offline data. Not all data was saved.',
|
||||||
|
buttons: [
|
||||||
|
'Okay'
|
||||||
|
],
|
||||||
|
}).then(tst => {
|
||||||
|
tst.present();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.initialized$.next(true);
|
this.initialized$.next(true);
|
||||||
|
|
||||||
if ( this.session.get('user.preferences.default_page') ) {
|
if ( this.session.get('user.preferences.default_page') ) {
|
||||||
@ -483,6 +522,44 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async doPrefetch() {
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loader = await this.loading.create({
|
||||||
|
message: 'Pre-fetching data...',
|
||||||
|
cssClass: 'noded-loading-mask',
|
||||||
|
showBackdrop: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise(res => setTimeout(res, 2000));
|
||||||
|
|
||||||
|
await this.loader.present();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (await this.api.needsSync()) {
|
||||||
|
this.loader.message = 'Syncing data...';
|
||||||
|
await this.api.syncOfflineData();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loader.message = 'Downloading data...';
|
||||||
|
await this.api.prefetchOfflineData();
|
||||||
|
} catch (e) {
|
||||||
|
const msg = await this.alerts.create({
|
||||||
|
header: 'Uh, oh!',
|
||||||
|
message: 'An unexpected error occurred while trying to sync offline data, and we were unable to continue.',
|
||||||
|
buttons: [
|
||||||
|
'OK',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await msg.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loader.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
toggleDark() {
|
toggleDark() {
|
||||||
// const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
// const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
this.darkMode = !this.darkMode;
|
this.darkMode = !this.darkMode;
|
||||||
@ -490,6 +567,10 @@ export class AppComponent implements OnInit {
|
|||||||
document.body.classList.toggle('dark', this.darkMode);
|
document.body.classList.toggle('dark', this.darkMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
togglePrefetch() {
|
||||||
|
this.session.set('user.preferences.auto_prefetch', !this.isPrefetch());
|
||||||
|
}
|
||||||
|
|
||||||
findNode(id: string, nodes = this.nodes) {
|
findNode(id: string, nodes = this.nodes) {
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
if ( node.id === id ) {
|
if ( node.id === id ) {
|
||||||
@ -508,4 +589,8 @@ export class AppComponent implements OnInit {
|
|||||||
isDark() {
|
isDark() {
|
||||||
return !!this.darkMode;
|
return !!this.darkMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPrefetch() {
|
||||||
|
return !!this.session.get('user.preferences.auto_prefetch');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,14 @@
|
|||||||
<i slot="start" class="fa" [ngClass]="isDark() ? 'fa-sun' : 'fa-moon'"></i>
|
<i slot="start" class="fa" [ngClass]="isDark() ? 'fa-sun' : 'fa-moon'"></i>
|
||||||
<ion-label>{{ isDark() ? 'To The Light!' : 'Go Dark...' }}</ion-label>
|
<ion-label>{{ isDark() ? 'To The Light!' : 'Go Dark...' }}</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
<ion-item button (click)="onSelect('prefetch_data')">
|
||||||
|
<i slot="start" class="fa fa-download"></i>
|
||||||
|
<ion-label>Prefetch Offline Data</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item button (click)="onSelect('toggle_auto_prefetch')">
|
||||||
|
<i slot="start" class="fa" [ngClass]="isPrefetch() ? 'fa-check-square' : 'fa-square'"></i>
|
||||||
|
<ion-label>Auto-Prefetch</ion-label>
|
||||||
|
</ion-item>
|
||||||
<ion-item button (click)="onSelect('logout')">
|
<ion-item button (click)="onSelect('logout')">
|
||||||
<i slot="start" class="fa fa-sign-out-alt"></i>
|
<i slot="start" class="fa fa-sign-out-alt"></i>
|
||||||
<ion-label>Logout</ion-label>
|
<ion-label>Logout</ion-label>
|
||||||
|
@ -2,7 +2,7 @@ import {Component, Input, OnInit} from '@angular/core';
|
|||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {ApiService} from '../../service/api.service';
|
import {ApiService} from '../../service/api.service';
|
||||||
import {PopoverController} from '@ionic/angular';
|
import {PopoverController} from '@ionic/angular';
|
||||||
import {DatabaseService} from "../../service/db/database.service";
|
import {DatabaseService} from '../../service/db/database.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-option-picker',
|
selector: 'app-option-picker',
|
||||||
@ -13,6 +13,9 @@ export class OptionPickerComponent implements OnInit {
|
|||||||
@Input() toggleDark: () => void;
|
@Input() toggleDark: () => void;
|
||||||
@Input() isDark: () => boolean;
|
@Input() isDark: () => boolean;
|
||||||
@Input() showSearch: () => void | Promise<void>;
|
@Input() showSearch: () => void | Promise<void>;
|
||||||
|
@Input() isPrefetch: () => boolean;
|
||||||
|
@Input() togglePrefetch: () => void;
|
||||||
|
@Input() doPrefetch: () => any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected api: ApiService,
|
protected api: ApiService,
|
||||||
@ -34,6 +37,11 @@ export class OptionPickerComponent implements OnInit {
|
|||||||
} else if ( key === 'search_everywhere' ) {
|
} else if ( key === 'search_everywhere' ) {
|
||||||
this.showSearch();
|
this.showSearch();
|
||||||
this.popover.dismiss();
|
this.popover.dismiss();
|
||||||
|
} else if ( key === 'toggle_auto_prefetch' ) {
|
||||||
|
this.togglePrefetch();
|
||||||
|
} else if ( key === 'prefetch_data' ) {
|
||||||
|
this.popover.dismiss();
|
||||||
|
this.doPrefetch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import {Database} from './db/Database';
|
|||||||
import {DatabaseColumn} from './db/DatabaseColumn';
|
import {DatabaseColumn} from './db/DatabaseColumn';
|
||||||
import {DatabaseEntry} from './db/DatabaseEntry';
|
import {DatabaseEntry} from './db/DatabaseEntry';
|
||||||
import {FileGroup} from './db/FileGroup';
|
import {FileGroup} from './db/FileGroup';
|
||||||
|
import {Page} from "./db/Page";
|
||||||
|
import {PageNode} from "./db/PageNode";
|
||||||
|
|
||||||
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.') {
|
||||||
@ -18,6 +20,12 @@ export class ResourceNotAvailableOfflineError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class OfflinePrefetchWouldOverwriteLocalDataError extends Error {
|
||||||
|
constructor(msg = 'Cannot run offline prefetch. There is un-synced local data that would be overwritten.') {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
@ -71,12 +79,21 @@ export class ApiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public makeOffline() {
|
public async makeOffline() {
|
||||||
|
const offlineKV = await this.db.getKeyValue('needs_online_sync');
|
||||||
|
offlineKV.data = true;
|
||||||
|
await offlineKV.save();
|
||||||
|
|
||||||
this.offline = true;
|
this.offline = true;
|
||||||
this.offline$.next(true);
|
this.offline$.next(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public makeOnline() {
|
public async needsSync() {
|
||||||
|
const offlineKV = await this.db.getKeyValue('needs_online_sync');
|
||||||
|
return Boolean(offlineKV.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async makeOnline() {
|
||||||
this.offline = false;
|
this.offline = false;
|
||||||
this.offline$.next(false);
|
this.offline$.next(false);
|
||||||
}
|
}
|
||||||
@ -183,6 +200,167 @@ export class ApiService {
|
|||||||
return `${this.baseEndpoint.endsWith('/') ? this.baseEndpoint.slice(0, -1) : this.baseEndpoint}${endpoint}`;
|
return `${this.baseEndpoint.endsWith('/') ? this.baseEndpoint.slice(0, -1) : this.baseEndpoint}${endpoint}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async syncOfflineData() {
|
||||||
|
const dirtyRecords = await this.db.getDirtyRecords();
|
||||||
|
await new Promise(async (res, rej) => {
|
||||||
|
this.post('/offline/sync', { dirtyRecords }).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
console.log('sync result', result);
|
||||||
|
res();
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async prefetchOfflineData(overwriteLocalData = false) {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
throw new ResourceNotAvailableOfflineError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !overwriteLocalData && await this.needsSync() ) {
|
||||||
|
throw new OfflinePrefetchWouldOverwriteLocalDataError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: any = await new Promise((res, rej) => {
|
||||||
|
this.get('/offline/prefetch').subscribe({
|
||||||
|
next: result => {
|
||||||
|
if ( !result.data ) {
|
||||||
|
return rej(new TypeError('Unable to parse data from API response.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( Array.isArray(data.pages) ) {
|
||||||
|
await this.db.pages.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.pages ) {
|
||||||
|
const page = new Page(
|
||||||
|
rec.UUID,
|
||||||
|
rec.Name,
|
||||||
|
rec.OrgUserId,
|
||||||
|
rec.IsPublic,
|
||||||
|
rec.IsVisibleInMenu,
|
||||||
|
rec.ParentId,
|
||||||
|
rec.NodeIds,
|
||||||
|
rec.CreatedAt,
|
||||||
|
rec.UpdatedAt,
|
||||||
|
rec.Active,
|
||||||
|
rec.CreatedUserId,
|
||||||
|
rec.UpdateUserId,
|
||||||
|
rec.ChildPageIds,
|
||||||
|
rec.noDelete,
|
||||||
|
rec.virtual
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(data.pageNodes) ) {
|
||||||
|
await this.db.pageNodes.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.pageNodes ) {
|
||||||
|
const node = new PageNode(
|
||||||
|
rec.UUID,
|
||||||
|
rec.Type,
|
||||||
|
rec.Value ? JSON.stringify(rec.Value) : JSON.stringify({}),
|
||||||
|
rec.PageId,
|
||||||
|
rec.CreatedAt,
|
||||||
|
rec.UpdatedAt,
|
||||||
|
rec.CreatedUserId,
|
||||||
|
rec.UpdateUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
await node.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(data.codiums) ) {
|
||||||
|
await this.db.codiums.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.codiums ) {
|
||||||
|
const code = new Codium(
|
||||||
|
rec.Language,
|
||||||
|
rec.NodeId,
|
||||||
|
rec.PageId,
|
||||||
|
rec.code,
|
||||||
|
rec.UUID
|
||||||
|
);
|
||||||
|
|
||||||
|
await code.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(data.databases) ) {
|
||||||
|
await this.db.databases.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.databases ) {
|
||||||
|
const db = new Database(
|
||||||
|
rec.Name,
|
||||||
|
rec.NodeId,
|
||||||
|
rec.PageId,
|
||||||
|
rec.ColumnIds,
|
||||||
|
rec.UUID,
|
||||||
|
rec.Active
|
||||||
|
);
|
||||||
|
|
||||||
|
await db.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(data.databaseColumns) ) {
|
||||||
|
await this.db.databaseColumns.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.databaseColumns ) {
|
||||||
|
const col = new DatabaseColumn(
|
||||||
|
rec.headerName,
|
||||||
|
rec.field,
|
||||||
|
rec.DatabaseId,
|
||||||
|
rec.UUID,
|
||||||
|
rec.Type,
|
||||||
|
rec.additionalData
|
||||||
|
);
|
||||||
|
|
||||||
|
await col.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(data.databaseEntries) ) {
|
||||||
|
await this.db.databaseEntries.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.databaseEntries ) {
|
||||||
|
const entry = new DatabaseEntry(
|
||||||
|
rec.DatabaseId,
|
||||||
|
JSON.stringify(rec.RowData || {}),
|
||||||
|
rec.UUID
|
||||||
|
);
|
||||||
|
|
||||||
|
await entry.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Array.isArray(data.fileGroups) ) {
|
||||||
|
await this.db.fileGroups.clear();
|
||||||
|
|
||||||
|
for ( const rec of data.fileGroups ) {
|
||||||
|
const group = new FileGroup(
|
||||||
|
rec.NodeId,
|
||||||
|
rec.PageId,
|
||||||
|
rec.FileIds,
|
||||||
|
JSON.stringify(rec.files || []),
|
||||||
|
rec.UUID
|
||||||
|
);
|
||||||
|
|
||||||
|
await group.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getDeviceToken(): Promise<string> {
|
public getDeviceToken(): Promise<string> {
|
||||||
return new Promise(async (res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
const tokenKV = await this.db.getKeyValue('device_token');
|
const tokenKV = await this.db.getKeyValue('device_token');
|
||||||
@ -677,7 +855,8 @@ export class ApiService {
|
|||||||
const newDatabaseEntry = new DatabaseEntry(
|
const newDatabaseEntry = new DatabaseEntry(
|
||||||
row.DatabaseId,
|
row.DatabaseId,
|
||||||
JSON.stringify(row.RowData),
|
JSON.stringify(row.RowData),
|
||||||
row.UUID || DatabaseEntry.getUUID()
|
row.UUID || DatabaseEntry.getUUID(),
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
await newDatabaseEntry.save();
|
await newDatabaseEntry.save();
|
||||||
@ -728,6 +907,7 @@ export class ApiService {
|
|||||||
def.UUID || DatabaseColumn.getUUID(),
|
def.UUID || DatabaseColumn.getUUID(),
|
||||||
def.Type,
|
def.Type,
|
||||||
def.additionalData,
|
def.additionalData,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
await newColumnDef.save();
|
await newColumnDef.save();
|
||||||
|
@ -60,7 +60,7 @@ export class DatabaseEntry extends Model<IDatabaseEntry> implements IDatabaseEnt
|
|||||||
|
|
||||||
public inflateToRecord() {
|
public inflateToRecord() {
|
||||||
const record = this.getSaveRecord();
|
const record = this.getSaveRecord();
|
||||||
record.RowData = JSON.parse(record.RowDataJSON);
|
record.RowData = record.RowDataJSON ? JSON.parse(record.RowDataJSON) : {};
|
||||||
delete record.RowDataJSON;
|
delete record.RowDataJSON;
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,18 @@ export abstract class Model<InterfaceType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async save() {
|
public async save() {
|
||||||
this.id = await this.getDatabase().put(this.getSaveRecord());
|
const record = this.getSaveRecord();
|
||||||
|
|
||||||
|
for ( const prop in record ) {
|
||||||
|
if ( !record.hasOwnProperty(prop) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( [true, false].includes(record[prop]) ) {
|
||||||
|
record[prop] = record[prop] ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.id = await this.getDatabase().put(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export interface IPage {
|
|||||||
ChildPageIds: string[];
|
ChildPageIds: string[];
|
||||||
noDelete: boolean;
|
noDelete: boolean;
|
||||||
virtual: boolean;
|
virtual: boolean;
|
||||||
needsServerUpdate?: boolean;
|
needsServerUpdate?: 0 | 1;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ export class Page extends Model<IPage> implements IPage {
|
|||||||
ChildPageIds: string[];
|
ChildPageIds: string[];
|
||||||
noDelete: boolean;
|
noDelete: boolean;
|
||||||
virtual: boolean;
|
virtual: boolean;
|
||||||
needsServerUpdate?: boolean;
|
needsServerUpdate?: 0 | 1;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
|
|
||||||
public static getTableName() {
|
public static getTableName() {
|
||||||
@ -66,7 +66,7 @@ export class Page extends Model<IPage> implements IPage {
|
|||||||
ChildPageIds: string[],
|
ChildPageIds: string[],
|
||||||
noDelete: boolean,
|
noDelete: boolean,
|
||||||
virtual: boolean,
|
virtual: boolean,
|
||||||
needsServerUpdate?: boolean,
|
needsServerUpdate?: 0 | 1,
|
||||||
deleted?: boolean,
|
deleted?: boolean,
|
||||||
id?: number
|
id?: number
|
||||||
) {
|
) {
|
||||||
|
@ -97,6 +97,27 @@ export class DatabaseService extends Dexie {
|
|||||||
this.pageNodes.mapToClass(PageNode);
|
this.pageNodes.mapToClass(PageNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return the local database records that need to be synced up with the server. */
|
||||||
|
public async getDirtyRecords() {
|
||||||
|
const codiums = await this.codiums.where({ needsServerUpdate: 1 }).toArray() as Codium[];
|
||||||
|
const databases = await this.databases.where({ needsServerUpdate: 1 }).toArray() as Database[];
|
||||||
|
const databaseColumns = await this.databaseColumns.where({ needsServerUpdate: 1 }).toArray() as DatabaseColumn[];
|
||||||
|
const databaseEntries = await this.databaseEntries.where({ needsServerUpdate: 1 }).toArray() as DatabaseEntry[];
|
||||||
|
const fileGroups = await this.fileGroups.where({ needsServerUpdate: 1 }).toArray() as FileGroup[];
|
||||||
|
const pages = await this.pages.where({ needsServerUpdate: 1 }).toArray() as Page[];
|
||||||
|
const pageNodes = await this.pageNodes.where({ needsServerUpdate: 1 }).toArray() as PageNode[];
|
||||||
|
|
||||||
|
return {
|
||||||
|
codiums: codiums.map(x => x.getSaveRecord()),
|
||||||
|
databases: databases.map(x => x.getSaveRecord()),
|
||||||
|
databaseColumns: databaseColumns.map(x => x.getSaveRecord()),
|
||||||
|
databaseEntries: databaseEntries.map(x => x.getSaveRecord()),
|
||||||
|
fileGroups: fileGroups.map(x => x.getSaveRecord()),
|
||||||
|
pages: pages.map(x => x.getSaveRecord()),
|
||||||
|
pageNodes: pageNodes.map(x => x.getSaveRecord()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async purge() {
|
public async purge() {
|
||||||
console.warn('Purging all local data!');
|
console.warn('Purging all local data!');
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ export class EditorService {
|
|||||||
if ( existingLocalPage ) {
|
if ( existingLocalPage ) {
|
||||||
existingLocalPage.fillFromRecord(page);
|
existingLocalPage.fillFromRecord(page);
|
||||||
existingLocalPage.UpdatedAt = String(new Date()); // FIXME update UpdateUserId
|
existingLocalPage.UpdatedAt = String(new Date()); // FIXME update UpdateUserId
|
||||||
existingLocalPage.needsServerUpdate = true;
|
existingLocalPage.needsServerUpdate = 1;
|
||||||
await existingLocalPage.save();
|
await existingLocalPage.save();
|
||||||
} else {
|
} else {
|
||||||
const newLocalPage = new Page(
|
const newLocalPage = new Page(
|
||||||
@ -196,7 +196,7 @@ export class EditorService {
|
|||||||
page.ChildPageIds,
|
page.ChildPageIds,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
await newLocalPage.save();
|
await newLocalPage.save();
|
||||||
@ -227,7 +227,7 @@ export class EditorService {
|
|||||||
result.data.ChildPageIds,
|
result.data.ChildPageIds,
|
||||||
result.data.noDelete,
|
result.data.noDelete,
|
||||||
result.data.virtual,
|
result.data.virtual,
|
||||||
false
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
await newLocalPage.save();
|
await newLocalPage.save();
|
||||||
@ -283,7 +283,7 @@ export class EditorService {
|
|||||||
|
|
||||||
page.NodeIds = nodeRecs.map(x => x.UUID);
|
page.NodeIds = nodeRecs.map(x => x.UUID);
|
||||||
existingLocalPage.NodeIds = nodeRecs.map(x => x.UUID);
|
existingLocalPage.NodeIds = nodeRecs.map(x => x.UUID);
|
||||||
existingLocalPage.needsServerUpdate = true;
|
existingLocalPage.needsServerUpdate = 1;
|
||||||
await existingLocalPage.save();
|
await existingLocalPage.save();
|
||||||
|
|
||||||
return res(nodeRecs.map(x => {
|
return res(nodeRecs.map(x => {
|
||||||
@ -356,7 +356,7 @@ export class EditorService {
|
|||||||
await newLocalNode.save();
|
await newLocalNode.save();
|
||||||
|
|
||||||
localPage.NodeIds.push(newLocalNode.UUID);
|
localPage.NodeIds.push(newLocalNode.UUID);
|
||||||
localPage.needsServerUpdate = true;
|
localPage.needsServerUpdate = 1;
|
||||||
await localPage.save();
|
await localPage.save();
|
||||||
|
|
||||||
const host = new HostRecord(nodeData.Value.Value);
|
const host = new HostRecord(nodeData.Value.Value);
|
||||||
@ -446,7 +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)
|
console.log('added node to page', host);
|
||||||
|
|
||||||
let placed = false;
|
let placed = false;
|
||||||
if ( position === 'before' && positionNodeId ) {
|
if ( position === 'before' && positionNodeId ) {
|
||||||
@ -542,7 +542,7 @@ export class EditorService {
|
|||||||
|
|
||||||
if ( existingLocalPage ) {
|
if ( existingLocalPage ) {
|
||||||
existingLocalPage.fillFromRecord(result.data);
|
existingLocalPage.fillFromRecord(result.data);
|
||||||
existingLocalPage.needsServerUpdate = false;
|
existingLocalPage.needsServerUpdate = 0;
|
||||||
await existingLocalPage.save();
|
await existingLocalPage.save();
|
||||||
} else {
|
} else {
|
||||||
const newLocalPage = new Page(
|
const newLocalPage = new Page(
|
||||||
@ -643,13 +643,13 @@ export class EditorService {
|
|||||||
[],
|
[],
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
const firstNode = new PageNode(
|
const firstNode = new PageNode(
|
||||||
PageNode.getUUID(),
|
PageNode.getUUID(),
|
||||||
'paragraph',
|
'paragraph',
|
||||||
JSON.stringify({ Value: 'Click to edit...' }),
|
JSON.stringify({ Value: 'Double-click to edit...' }),
|
||||||
page.UUID,
|
page.UUID,
|
||||||
String(new Date()),
|
String(new Date()),
|
||||||
String(new Date()),
|
String(new Date()),
|
||||||
|
Loading…
Reference in New Issue
Block a user