Add offline caching for code editor contents
This commit is contained in:
@@ -6,6 +6,13 @@ import ApiResponse from '../structures/ApiResponse';
|
||||
import {MenuItem} from './db/MenuItem';
|
||||
import {DatabaseService} from './db/database.service';
|
||||
import {ConnectionService} from 'ng-connection-service';
|
||||
import {Codium} from './db/Codium';
|
||||
|
||||
export class ResourceNotAvailableOfflineError extends Error {
|
||||
constructor(msg = 'This resource is not yet available offline on this device.') {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -221,4 +228,163 @@ export class ApiService {
|
||||
res();
|
||||
});
|
||||
}
|
||||
|
||||
public deleteCodium(PageId: string, NodeId: string, CodiumId: string): Promise<void> {
|
||||
return new Promise(async (res, rej) => {
|
||||
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();
|
||||
const existingLocalCodium = existingLocalCodiums.length > 0 ? existingLocalCodiums[0] as Codium : undefined;
|
||||
|
||||
if ( this.isOffline ) {
|
||||
if ( existingLocalCodium ) {
|
||||
existingLocalCodium.deleted = true;
|
||||
existingLocalCodium.needsServerUpdate = true;
|
||||
await existingLocalCodium.save();
|
||||
return res();
|
||||
} else {
|
||||
return rej(new ResourceNotAvailableOfflineError());
|
||||
}
|
||||
}
|
||||
|
||||
this.post(`/code/${PageId}/${NodeId}/delete/${CodiumId}`).subscribe({
|
||||
next: async result => {
|
||||
if ( existingLocalCodium ) {
|
||||
await this.db.codiums.delete(existingLocalCodium.id);
|
||||
}
|
||||
res();
|
||||
},
|
||||
error: rej,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public saveCodium(PageId: string, NodeId: string, CodiumId: string, data: any): Promise<any> {
|
||||
return new Promise(async (res, rej) => {
|
||||
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();
|
||||
const existingLocalCodium = existingLocalCodiums.length > 0 ? existingLocalCodiums[0] as Codium : undefined;
|
||||
|
||||
// If we're offline, update or create the local record
|
||||
if ( this.isOffline ) {
|
||||
if ( existingLocalCodium ) {
|
||||
existingLocalCodium.fillFromRecord(data);
|
||||
existingLocalCodium.needsServerUpdate = true;
|
||||
|
||||
await existingLocalCodium.save();
|
||||
return res(existingLocalCodium.getSaveRecord());
|
||||
} else {
|
||||
const newLocalCodium = new Codium(
|
||||
data.Language,
|
||||
NodeId,
|
||||
PageId,
|
||||
data.code,
|
||||
data.UUID || Codium.getUUID(),
|
||||
true,
|
||||
);
|
||||
|
||||
await newLocalCodium.save();
|
||||
return res(newLocalCodium.getSaveRecord());
|
||||
}
|
||||
}
|
||||
|
||||
// If we're online, save the data and update our local records
|
||||
this.post(`/code/${PageId}/${NodeId}/set/${CodiumId}`, data).subscribe({
|
||||
next: async result => {
|
||||
if ( existingLocalCodium ) {
|
||||
existingLocalCodium.fillFromRecord(result.data);
|
||||
existingLocalCodium.needsServerUpdate = false;
|
||||
|
||||
await existingLocalCodium.save();
|
||||
return res(result.data);
|
||||
} else {
|
||||
const newLocalCodium = new Codium(
|
||||
result.data.Language,
|
||||
result.data.NodeId,
|
||||
result.data.PageId,
|
||||
result.data.code,
|
||||
result.data.UUID,
|
||||
);
|
||||
|
||||
await newLocalCodium.save();
|
||||
return res(result.data);
|
||||
}
|
||||
},
|
||||
error: rej,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public createCodium(PageId: string, NodeId: string): Promise<any> {
|
||||
return new Promise(async (res, rej) => {
|
||||
// If offline, create a new local DB record
|
||||
if ( this.isOffline ) {
|
||||
const newLocalCodium = new Codium(
|
||||
'javascript',
|
||||
NodeId,
|
||||
PageId,
|
||||
'',
|
||||
Codium.getUUID(),
|
||||
true
|
||||
);
|
||||
|
||||
await newLocalCodium.save();
|
||||
return res(newLocalCodium.getSaveRecord());
|
||||
}
|
||||
|
||||
// If online, create a new record on the server and sync it to the local db
|
||||
this.post(`/code/${PageId}/${NodeId}/create`).subscribe({
|
||||
next: async result => {
|
||||
const newLocalCodium = new Codium(
|
||||
result.data.Language,
|
||||
result.data.NodeId,
|
||||
result.data.PageId,
|
||||
result.data.code,
|
||||
result.data.UUID,
|
||||
);
|
||||
|
||||
await newLocalCodium.save();
|
||||
res(result.data);
|
||||
},
|
||||
error: rej,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getCodium(PageId: string, NodeId: string, CodiumId: string): Promise<any> {
|
||||
return new Promise(async (res, rej) => {
|
||||
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();
|
||||
const existingLocalCodium = existingLocalCodiums.length > 0 ? existingLocalCodiums[0] as Codium : undefined;
|
||||
|
||||
// If offline, try to load it from the local DB
|
||||
if ( this.isOffline ) {
|
||||
if ( existingLocalCodium ) {
|
||||
return res(existingLocalCodium.getSaveRecord());
|
||||
} else {
|
||||
return rej(new ResourceNotAvailableOfflineError());
|
||||
}
|
||||
}
|
||||
|
||||
// If online, fetch the codium and store/update it locally
|
||||
this.get(`/code/${PageId}/${NodeId}/get/${CodiumId}`).subscribe({
|
||||
next: async result => {
|
||||
if ( existingLocalCodium ) {
|
||||
existingLocalCodium.fillFromRecord(result.data);
|
||||
|
||||
await existingLocalCodium.save();
|
||||
return res(result.data);
|
||||
} else {
|
||||
const newLocalCodium = new Codium(
|
||||
result.data.Language,
|
||||
result.data.NodeId,
|
||||
result.data.PageId,
|
||||
result.data.code,
|
||||
result.data.UUID
|
||||
);
|
||||
|
||||
await newLocalCodium.save();
|
||||
return res(result.data);
|
||||
}
|
||||
},
|
||||
error: rej,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
87
src/app/service/db/Codium.ts
Normal file
87
src/app/service/db/Codium.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import {Model} from './Model';
|
||||
|
||||
export interface ICodium {
|
||||
id?: number;
|
||||
Language: string;
|
||||
NodeId: string;
|
||||
PageId: string;
|
||||
code: string;
|
||||
UUID: string;
|
||||
needsServerUpdate?: boolean;
|
||||
deleted?: boolean;
|
||||
}
|
||||
|
||||
export class Codium extends Model<ICodium> implements ICodium {
|
||||
id?: number;
|
||||
Language: string;
|
||||
NodeId: string;
|
||||
PageId: string;
|
||||
code: string;
|
||||
UUID: string;
|
||||
needsServerUpdate?: boolean;
|
||||
deleted?: boolean;
|
||||
|
||||
public static getTableName() {
|
||||
return 'codiums';
|
||||
}
|
||||
|
||||
public static getSchema() {
|
||||
return '++id, Language, NodeId, PageId, code, UUID, needsServerUpdate, deleted';
|
||||
}
|
||||
|
||||
constructor(
|
||||
Language: string,
|
||||
NodeId: string,
|
||||
PageId: string,
|
||||
code: string,
|
||||
UUID: string,
|
||||
needsServerUpdate?: boolean,
|
||||
deleted?: boolean,
|
||||
id?: number
|
||||
) {
|
||||
super();
|
||||
|
||||
this.Language = Language;
|
||||
this.NodeId = NodeId;
|
||||
this.PageId = PageId;
|
||||
this.code = code;
|
||||
this.UUID = UUID;
|
||||
|
||||
if ( typeof needsServerUpdate !== 'undefined' ) {
|
||||
this.needsServerUpdate = needsServerUpdate;
|
||||
}
|
||||
|
||||
if ( typeof deleted !== 'undefined' ) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
if ( id ) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public fillFromRecord(record: any) {
|
||||
this.Language = record.Language;
|
||||
this.NodeId = record.NodeId;
|
||||
this.PageId = record.PageId;
|
||||
this.code = record.code;
|
||||
this.UUID = record.UUID;
|
||||
}
|
||||
|
||||
public getSaveRecord(): any {
|
||||
return {
|
||||
...(this.id ? { id: this.id } : {}),
|
||||
Language: this.Language,
|
||||
NodeId: this.NodeId,
|
||||
PageId: this.PageId,
|
||||
code: this.code,
|
||||
UUID: this.UUID,
|
||||
...(typeof this.needsServerUpdate === 'undefined' ? {} : { needsServerUpdate: this.needsServerUpdate }),
|
||||
...(typeof this.deleted === 'undefined' ? {} : { deleted: this.deleted }),
|
||||
};
|
||||
}
|
||||
|
||||
public getDatabase(): Dexie.Table<ICodium, number> {
|
||||
return this.staticClass().dbService.table('codiums') as Dexie.Table<ICodium, number>;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Dexie from 'dexie';
|
||||
import {DatabaseService} from './database.service';
|
||||
import {uuid_v4} from '../../utility';
|
||||
|
||||
export abstract class Model<InterfaceType> {
|
||||
public static dbService?: DatabaseService;
|
||||
@@ -14,6 +15,10 @@ export abstract class Model<InterfaceType> {
|
||||
throw new TypeError('Child class must implement.');
|
||||
}
|
||||
|
||||
public static getUUID(): string {
|
||||
return uuid_v4();
|
||||
}
|
||||
|
||||
public abstract getDatabase(): Dexie.Table<InterfaceType, number>;
|
||||
public abstract getSaveRecord(): any;
|
||||
|
||||
|
||||
@@ -3,17 +3,19 @@ import Dexie from 'dexie';
|
||||
import {IMigration, Migration} from './Migration';
|
||||
import {IMenuItem, MenuItem} from './MenuItem';
|
||||
import {KeyValue, IKeyValue} from './KeyValue';
|
||||
import {Codium, ICodium} from './Codium';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DatabaseService extends Dexie {
|
||||
protected static registeredModels = [Migration, MenuItem, KeyValue];
|
||||
protected static registeredModels = [Migration, MenuItem, KeyValue, Codium];
|
||||
protected initialized = false;
|
||||
|
||||
migrations!: Dexie.Table<IMigration, number>;
|
||||
menuItems!: Dexie.Table<IMenuItem, number>;
|
||||
keyValues!: Dexie.Table<IKeyValue, number>;
|
||||
codiums!: Dexie.Table<ICodium, number>;
|
||||
|
||||
constructor(
|
||||
) {
|
||||
@@ -46,7 +48,7 @@ export class DatabaseService extends Dexie {
|
||||
schema[ModelClass.getTableName()] = ModelClass.getSchema();
|
||||
}
|
||||
|
||||
await this.version(3).stores(schema);
|
||||
await this.version(5).stores(schema);
|
||||
await this.open();
|
||||
|
||||
this.migrations = this.table('migrations');
|
||||
@@ -58,10 +60,7 @@ export class DatabaseService extends Dexie {
|
||||
this.keyValues = this.table('keyValues');
|
||||
this.keyValues.mapToClass(KeyValue);
|
||||
|
||||
// await new Promise(res => {
|
||||
// setTimeout(() => {
|
||||
// res();
|
||||
// }, 1000);
|
||||
// });
|
||||
this.codiums = this.table('codiums');
|
||||
this.codiums.mapToClass(Codium);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user