|
|
|
@ -11,6 +11,8 @@ import {Database} from './db/Database';
|
|
|
|
|
import {DatabaseColumn} from './db/DatabaseColumn';
|
|
|
|
|
import {DatabaseEntry} from './db/DatabaseEntry';
|
|
|
|
|
import {FileGroup} from './db/FileGroup';
|
|
|
|
|
import {Page} from "./db/Page";
|
|
|
|
|
import {PageNode} from "./db/PageNode";
|
|
|
|
|
|
|
|
|
|
export class ResourceNotAvailableOfflineError extends Error {
|
|
|
|
|
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({
|
|
|
|
|
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$.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$.next(false);
|
|
|
|
|
}
|
|
|
|
@ -183,6 +200,167 @@ export class ApiService {
|
|
|
|
|
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> {
|
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
|
const tokenKV = await this.db.getKeyValue('device_token');
|
|
|
|
@ -677,7 +855,8 @@ export class ApiService {
|
|
|
|
|
const newDatabaseEntry = new DatabaseEntry(
|
|
|
|
|
row.DatabaseId,
|
|
|
|
|
JSON.stringify(row.RowData),
|
|
|
|
|
row.UUID || DatabaseEntry.getUUID()
|
|
|
|
|
row.UUID || DatabaseEntry.getUUID(),
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await newDatabaseEntry.save();
|
|
|
|
@ -728,6 +907,7 @@ export class ApiService {
|
|
|
|
|
def.UUID || DatabaseColumn.getUUID(),
|
|
|
|
|
def.Type,
|
|
|
|
|
def.additionalData,
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await newColumnDef.save();
|
|
|
|
|