Skip to content

Commit

Permalink
wip(#14): Dexie ops
Browse files Browse the repository at this point in the history
  • Loading branch information
pokeghosst committed Oct 13, 2024
1 parent 3bf1480 commit b21c440
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 47 deletions.
94 changes: 73 additions & 21 deletions pokebook/src/lib/driver/PoemLocalStorageDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,81 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import { XMLBuilder, XMLParser } from 'fast-xml-parser';
import { Filesystem } from '../plugins/Filesystem';
import type { PoemFileEntity, PoemEntity } from '../types';
import type { IPoemStorageDriver } from './IPoemStorageDriver';

async function resolveImplementation() {
// TODO: Revise this. There has to be a more orthodox way to do this check
if (window.__TAURI_INTERNALS__) {
console.log('Loading filesystem driver');
const { FilesystemStorageDriver } = await import('./FilesystemStorageDriver');
return FilesystemStorageDriver;
} else {
console.log('Loading web storage driver');
const { WebStorageDriver } = await import('./WebStorageDriver');
return WebStorageDriver;
}
}
// import type { IPoemStorageDriver } from './IPoemStorageDriver';

// declare global {
// interface Window {
// __TAURI_INTERNAL__?: Record<string, unknown>;
// }
// }

// // I am NOT dealing with this just to have "end-to-end type safety" or whatever.
// // If it works correctly, it's all that matters.
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
// let providerPromise: any;

// async function getImplementation() {
// if (!providerPromise) {
// if (window.__TAURI_INTERNAL__) {
// providerPromise = import('./FilesystemStorageDriver').then(
// (module) => module.FilesystemStorageDriver
// );
// } else {
// providerPromise = import('./WebStorageDriver').then((module) => module.WebStorageDriver);
// }
// }
// return providerPromise;
// }

let PoemLocalStorageDriver: IPoemStorageDriver;
// export const PoemLocalStorageDriver = new Proxy(
// {},
// {
// get(_, prop) {
// return async (...args: unknown[]) => {
// const impl = await getImplementation();
// const method = impl[prop];
// if (typeof method === 'function') {
// return method.apply(impl, args);
// } else {
// throw new Error(`Method ${String(prop)} does not exist on implementation`);
// }
// };
// }
// }
// ) as IPoemStorageDriver;

(async () => {
const impl = await resolveImplementation();
PoemLocalStorageDriver = impl;
})().catch((error) => {
console.error('Failed to initialize the driver:', error);
throw error;
});
export const PoemLocalStorageDriver: IPoemStorageDriver = {
listPoems: function (): Promise<PoemFileEntity[]> {
const files = Filesystem.readDir({ path: '/' }).entries;
console.log(files);
},
loadPoem: async function (poemUri: string): Promise<PoemEntity> {
console.log('loading poem');

export { PoemLocalStorageDriver };
const file = await Filesystem.readFile({ path: `/${poemUri}` });
console.log(file);

return new XMLParser().parse(file.data);
},
savePoem: async function (poem: PoemEntity): Promise<{ id: string; timestamp: number }> {
const now = Date.now();

const { uri } = await Filesystem.writeFile({
path: `/${poem.name}_${now}.xml`,
data: new XMLBuilder({ format: true }).build(poem)
});

return { id: uri, timestamp: now };
},
updatePoem: function (poem: PoemEntity, poemUri: string): Promise<string | void> {
throw new Error('Function not implemented.');
},
deletePoem: function (poemUri: string): Promise<void> {
throw new Error('Function not implemented.');
}
};
31 changes: 5 additions & 26 deletions pokebook/src/lib/driver/WebStorageDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import { Encoding } from '@capacitor/filesystem';

import Dexie, { type EntityTable } from 'dexie';
import { XMLBuilder } from 'fast-xml-parser';

import FilesystemWithPermissions from '../util/FilesystemWithPermissions';

import type { PoemEntity } from '$lib/types';
import type { IPoemStorageDriver } from './IPoemStorageDriver';
Expand Down Expand Up @@ -75,30 +70,14 @@ export const WebStorageDriver: IPoemStorageDriver = {
const timestamp = Date.now();
const id = (await db.poems.add({ ...poem, timestamp })).toString();

return {
id,
timestamp
};
return { id, timestamp };
},
updatePoem: async function (poem: PoemEntity, poemUri: string) {
await FilesystemWithPermissions.writeFile({
path: poemUri,
data: new XMLBuilder({ format: true }).build(poem),
encoding: Encoding.UTF8
});
const directory = poemUri.split('poems/')[0];
const timestamp = poemUri.split('poems/')[1].split(/_|\.xml/)[1];
const newFileUri = `${directory}poems/${poem.name}_${timestamp}.xml`;
await FilesystemWithPermissions.rename({
from: poemUri,
to: newFileUri
});

return newFileUri;
await db.poems.update(parseInt(poemUri), poem);
},
deletePoem: async function (poemUri: string): Promise<void> {
await FilesystemWithPermissions.deleteFile({
path: poemUri
});
console.log('deleting poem', Number(poemUri));

await db.poems.delete(parseInt(poemUri));
}
};
54 changes: 54 additions & 0 deletions pokebook/src/lib/plugins/FilesystemPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
export interface FilesystemPlugin {
exists(options: ExistsOptions): Promise<ExistsResult>;
writeFile(options: WriteFileOptions): Promise<WriteFileResult>;
readFile(options: ReadFileOptions): Promise<ReadFileResult>;
readDir(options: ReadDirOptions): Promise<ReadDirResult>;
}

export interface ExistsOptions {
path: string;
}

export interface ExistsResult {
exists: boolean;
}

export interface WriteFileOptions {
path: string;
data: string;
}

export interface WriteFileResult {
uri: string;
}

export interface ReadFileOptions {
path: string;
}

export interface ReadFileResult {
data: string;
}

export interface FilesystemFile {
content: string;
path: string;
ctime: number;
mtime: number;
}

export interface ReadDirOptions {
path: string;
}

export interface ReadDirResult {
entries: FileInfo[];
}

export interface FileInfo {
name: string;
type: 'file' | 'dir';
ctime: number;
mtime: number;
uri: string;
}
13 changes: 13 additions & 0 deletions pokebook/src/lib/plugins/FilesystemTauri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class FilesystemTauri implements FilesystemPlugin {
exists(path: string): Promise<boolean> {
throw new Error('Method not implemented.');
}
async writeFile(path: string, data: string): Promise<WriteFileResult> {
await writeTextFile(path, data, {
baseDir: BaseDirectory.Home
});
}
readFile(path: string): Promise<string> {
throw new Error('Method not implemented.');
}
}
100 changes: 100 additions & 0 deletions pokebook/src/lib/plugins/FilesystemWeb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
PokeBook -- Pokeghost's poetry noteBook
Copyright (C) 2024 Pokeghost.
PokeBook is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PokeBook is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import Dexie, { type EntityTable } from 'dexie';

import type {
ExistsOptions,
ExistsResult,
FileInfo,
FilesystemFile,
FilesystemPlugin,
ReadDirOptions,
ReadDirResult,
ReadFileOptions,
ReadFileResult,
WriteFileOptions,
WriteFileResult
} from './FilesystemPlugin';

type DexieFilesystem = Dexie & { files: EntityTable<FilesystemFile, 'path'> };

export class FilesystemWeb implements FilesystemPlugin {
private _db?: DexieFilesystem;

private getDb(): DexieFilesystem {
if (this._db !== undefined) {
return this._db;
}

this._db = new Dexie('PokeBook') as DexieFilesystem;
this._db.version(1).stores({ files: 'path, content, ctime, mtime' });

return this._db;
}
async exists(options: ExistsOptions): Promise<ExistsResult> {
const count = await this.getDb().files.where('path').equals(options.path).count();
return { exists: count > 0 };
}
async writeFile(options: WriteFileOptions): Promise<WriteFileResult> {
const now = Date.now();

const uri = await this.getDb().files.add(
{
content: options.data,
path: options.path,
ctime: now,
mtime: now
},
options.path
);
return { uri };
}
async readFile(options: ReadFileOptions): Promise<ReadFileResult> {
const data = await this.getDb().files.where('path').equals(options.path).first();

console.log(data);

if (data?.content) {
return { data: data.content };
} else {
// TODO: Handle properly
throw new Error('errors.unknown');
}
}
async readDir(options: ReadDirOptions): Promise<ReadDirResult> {
console.log('files', await this.getDb().files.where('path').startsWith(options.path).toArray());

const entries = await this.getDb().files.where('path').startsWith(options.path).toArray();

console.log(entries);

return {
entries: entries.map(
(file) =>
({
name: file.path,
type: 'file',
ctime: file.ctime,
mtime: file.mtime,
uri: file.path
} as FileInfo)
)
};
}
}
Empty file.
Empty file.

0 comments on commit b21c440

Please sign in to comment.