export interface DbConfig {
/** prefix for indexed db name */
name: string;
/** keyPath in indexed db */
keyPath?: string;
/** keyPath of indexed db */
objectName: string;
}
@Injectable()
export class DbService<T = any> {
private version = 1;
private db$: Observable<IDBDatabase> = this.createDb(this.config.name, this.version);
constructor(
@Inject(DB_CONFIG) private config: DbConfig,
) { }
private createDb(name: string, version?: number): Observable<IDBDatabase> {
const openRequest: IDBOpenDBRequest = indexedDB.open(name, version);
openRequest.onupgradeneeded = (evt: IDBVersionChangeEvent) => this.onUpgradeNeeded(openRequest);
return this.fromIDBRequest(openRequest).pipe(
shareReplay(1),
);
}
private onUpgradeNeeded(openRequest: IDBOpenDBRequest): void {
const db = openRequest.result;
if (db.objectStoreNames.contains(this.config.objectName)) {
return;
}
db.createObjectStore(this.config.objectName, { keyPath: this.config.keyPath });
}
public save(value: T, key?: IDBValidKey): Observable<IDBValidKey> {
return this.db$.pipe(
mergeMap((db: IDBDatabase) => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readwrite').put(value, key)
)),
);
}
public saveAll(values: T[]): Observable<IDBValidKey[]> {
return from(values).pipe(
mergeMap((value: T, index: number) => this.save(value, index)),
toArray(),
);
}
public delete(key: string): Observable<undefined> {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readwrite').delete(key),
))
);
}
public clear() {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readwrite').clear(),
))
);
}
public retreive(key: string): Observable<T> {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readonly').get(key)
)),
);
}
public retreiveAll(): Observable<T[]> {
return this.db$.pipe(
mergeMap(db => this.fromIDBRequest(
this.createIDBObjectStore(db, 'readonly').getAll()
)),
);
}
private createIDBObjectStore(db: IDBDatabase, mode: IDBTransactionMode): IDBObjectStore {
const transaction: IDBTransaction = db.transaction(this.config.objectName, mode);
return transaction.objectStore(this.config.objectName);
}
private fromIDBRequest<R>(idbRequest: IDBRequest<R>): Observable<R> {
return new Observable<R>(observer => {
idbRequest.onsuccess = (evt: Event) => {
observer.next(idbRequest.result);
observer.complete();
evt.stopPropagation();
};
idbRequest.onerror = (evt: Event) => {
observer.error(idbRequest.error);
evt.stopPropagation();
};
});
}
public selectDb() {
return this.db$;
}
}