Files
readest/apps/readest-app/src/services/nativeAppService.ts
T
2024-10-12 21:14:04 +02:00

199 lines
5.7 KiB
TypeScript

import {
exists,
mkdir,
readTextFile,
readFile,
writeTextFile,
writeFile,
readDir,
remove,
BaseDirectory,
} from '@tauri-apps/plugin-fs';
import { type as osType } from '@tauri-apps/plugin-os';
import {
join,
appConfigDir,
appDataDir,
appCacheDir,
appLogDir,
documentDir,
} from '@tauri-apps/api/path';
import { convertFileSrc } from '@tauri-apps/api/core';
import { open, message } from '@tauri-apps/plugin-dialog';
import { Book } from '../types/book';
import { SystemSettings } from '../types/settings';
import { AppService, BaseDir, ToastType } from '../types/system';
const IS_MOBILE = osType() === 'ios' || osType() === 'android';
const BOOKS_SUBDIR = 'DigestLibrary/Books';
let BOOKS_DIR = '';
function resolvePath(
fp: string,
base: BaseDir,
): { baseDir: number; base: BaseDir; fp: string; dir: () => Promise<string> } {
switch (base) {
case 'Settings':
return { baseDir: BaseDirectory.AppConfig, fp, base, dir: appConfigDir };
case 'Data':
return { baseDir: BaseDirectory.AppData, fp, base, dir: appDataDir };
case 'Cache':
return { baseDir: BaseDirectory.AppCache, fp, base, dir: appCacheDir };
case 'Log':
return { baseDir: BaseDirectory.AppLog, fp, base, dir: appLogDir };
case 'Books':
return {
baseDir: IS_MOBILE ? BaseDirectory.AppData : BaseDirectory.Document,
fp: `${BOOKS_SUBDIR}/${fp}`,
base,
dir: () => new Promise((r) => r(`${BOOKS_DIR}/`)),
};
default:
return {
baseDir: BaseDirectory.Temp,
fp,
base,
dir: () => new Promise((r) => r('')),
};
}
}
const SETTINGS_PATH = resolvePath('settings.json', 'Settings');
export const nativeAppService: AppService = {
fs: {
async readFile(path: string, base: BaseDir, mode: 'text' | 'binary') {
const { fp, baseDir } = resolvePath(path, base);
return mode === 'text'
? (readTextFile(fp, base && { baseDir }) as Promise<string>)
: (await readFile(fp, base && { baseDir })).buffer;
},
async writeFile(path: string, base: BaseDir, content: string | ArrayBuffer) {
const { fp, baseDir } = resolvePath(path, base);
return typeof content === 'string'
? writeTextFile(fp, content, base && { baseDir })
: writeFile(fp, new Uint8Array(content), base && { baseDir });
},
async removeFile(path: string, base: BaseDir) {
const { fp, baseDir } = resolvePath(path, base);
return remove(fp, base && { baseDir });
},
async createDir(path: string, base: BaseDir, recursive = false) {
const { fp, baseDir } = resolvePath(path, base);
await mkdir(fp, base && { baseDir, recursive });
},
async removeDir(path: string, base: BaseDir, recursive = false) {
const { fp, baseDir } = resolvePath(path, base);
await remove(fp, base && { baseDir, recursive });
},
async readDir(path: string, base: BaseDir) {
const { fp, baseDir } = resolvePath(path, base);
const list = await readDir(fp, base && { baseDir });
return list.map((entity) => {
return {
path: entity.name,
isDir: entity.isDirectory,
};
});
},
async exists(path: string, base: BaseDir) {
const { fp, baseDir } = resolvePath(path, base);
try {
const res = await exists(fp, base && { baseDir });
return res;
} catch {
return false;
}
},
},
loadSettings: async () => {
let settings: SystemSettings;
const { fp, base } = SETTINGS_PATH;
try {
await nativeAppService.fs.exists(fp, base);
const txt = await nativeAppService.fs.readFile(fp, base, 'text');
settings = JSON.parse(txt as string);
} catch {
const INIT_BOOKS_DIR = await join(
IS_MOBILE ? await appDataDir() : await documentDir(),
BOOKS_SUBDIR,
);
settings = {
localBooksDir: INIT_BOOKS_DIR,
globalReadSettings: {
themeType: 'auto',
fontFamily: '',
fontSize: 1.0,
wordSpacing: 0.16,
lineSpacing: 1.5,
},
};
await nativeAppService.fs.createDir('', base, true);
await nativeAppService.fs.writeFile(fp, base, JSON.stringify(settings));
}
BOOKS_DIR = settings.localBooksDir;
return settings;
},
saveSettings: async (settings: SystemSettings) => {
const { fp, base } = SETTINGS_PATH;
await nativeAppService.fs.createDir('', base, true);
await nativeAppService.fs.writeFile(fp, base, JSON.stringify(settings));
BOOKS_DIR = settings.localBooksDir;
},
selectDirectory: async (title: string) => {
const selected = await open({
title,
directory: true,
});
return selected as string;
},
selectFiles: async (name: string, extensions: string[]) => {
const selected = await open({
multiple: true,
filters: [{ name, extensions }],
});
if (Array.isArray(selected)) {
return selected;
} else if (selected === null) {
return [];
} else {
return [selected];
}
},
showMessage: async (msg: string, kind: ToastType = 'info', title?: string, okLabel?: string) => {
await message(msg, { kind, title, okLabel });
},
loadLibraryBooks: async () => {
let books: Book[] = [];
try {
const txt = await nativeAppService.fs.readFile('books.json', 'Books', 'text');
books = JSON.parse(txt as string);
} catch {
await nativeAppService.fs.writeFile('books.json', 'Books', '[]');
}
books.forEach((book) => {
book.coverImageUrl = nativeAppService.generateCoverUrl(book);
});
return books;
},
generateCoverUrl: (book: Book) => {
return convertFileSrc(`${BOOKS_DIR}/${book.id}/cover.png`);
},
};