building out settings db

finally got transactions working too
This commit is contained in:
austinried 2021-06-17 13:01:47 +09:00
parent 2a6821c25d
commit de5b479c47
9 changed files with 240 additions and 71 deletions

View File

@ -1,10 +1,10 @@
import React from 'react';
import { RecoilRoot } from 'recoil';
import ArtistsList from './src/components/ArtistsList';
import SettingsView from './src/components/Settings';
const App = () => (
<RecoilRoot>
<ArtistsList />
<SettingsView />
</RecoilRoot>
);

6
src/clients.ts Normal file
View File

@ -0,0 +1,6 @@
import { MusicDb } from "./storage/music";
import { SettingsDb } from "./storage/settings";
import { SubsonicApiClient } from "./subsonic/api";
export const musicDb = new MusicDb();
export const settingsDb = new SettingsDb();

View File

@ -0,0 +1,37 @@
import React from 'react';
import { Button, View } from 'react-native';
import { musicDb, settingsDb } from '../clients';
const DbControls = () => {
const recreateMusicDb = async () => {
try { await musicDb.deleteDb(); } catch {}
await musicDb.createDb();
}
const recreateSettingsDb = async () => {
try { await settingsDb.deleteDb(); } catch {}
await settingsDb.createDb();
}
return (
<View>
<Button
title='Recreate Music DB'
onPress={recreateMusicDb}
/>
<Button
title='Recreate Settings DB'
onPress={recreateSettingsDb}
/>
</View>
);
}
const SettingsView = () => (
<View>
<DbControls />
</View>
)
export default SettingsView;

View File

@ -1,67 +0,0 @@
import SQLite, { DatabaseParams, ResultSet, SQLiteDatabase } from 'react-native-sqlite-storage';
SQLite.enablePromise(true);
abstract class DbStorage {
private dbParams: DatabaseParams;
constructor(dbParams: DatabaseParams) {
this.dbParams = dbParams;
}
abstract createDb(): Promise<void>
private async openDb(): Promise<SQLiteDatabase> {
return await SQLite.openDatabase({ ...this.dbParams });
}
async deleteDb(): Promise<void> {
await SQLite.deleteDatabase({ ...this.dbParams });
}
async executeSql(sql: string, params?: any[]): Promise<[ResultSet]> {
const db = await this.openDb();
try {
// https://github.com/andpor/react-native-sqlite-storage/issues/410
// if (params) {
// for (const p of params) {
// if (Array.isArray(p)) {
// throw new Error('param value cannot be an array');
// }
// }
// }
return await db.executeSql(sql, params);
} catch (err) {
try { await db.close(); } catch {}
throw err;
} finally {
try { await db.close(); } catch {}
}
}
}
export class MusicDb extends DbStorage {
constructor() {
super({ name: 'music.db', location: 'default' });
}
async createDb(): Promise<void> {
await this.executeSql(`
CREATE TABLE artists (
id PRIMARY KEY NOT NULL,
name NOT NULL,
starred
);
`);
}
}
class SettingsDb extends DbStorage {
constructor() {
super({ name: 'settings.db', location: 'Library' });
}
async createDb(): Promise<void> {
return;
}
}

View File

@ -1,7 +1,7 @@
import md5 from 'md5';
import { atom, selector, useSetRecoilState } from 'recoil';
import { SubsonicApiClient } from '../subsonic/client';
import { MusicDb } from '../db/client';
import { SubsonicApiClient } from '../subsonic/api';
import { MusicDb } from '../storage/music';
const db = new MusicDb();

71
src/storage/db.ts Normal file
View File

@ -0,0 +1,71 @@
import SQLite, { DatabaseParams, ResultSet, SQLiteDatabase, Transaction } from 'react-native-sqlite-storage';
SQLite.enablePromise(true);
export abstract class DbStorage {
private dbParams: DatabaseParams;
constructor(dbParams: DatabaseParams) {
this.dbParams = dbParams;
}
// abstract createDb(): Promise<void>
protected async createDb(scope: (tx: Transaction) => void): Promise<void> {
await this.transaction(tx => {
tx.executeSql(`
CREATE TABLE db_version (
version INTEGER NOT NULL
);
`);
tx.executeSql(`
INSERT INTO db_version (version)
VALUES (?);
`, [1]);
scope(tx);
});
}
async dbExists(): Promise<boolean> {
const results = await this.executeSql(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='db_version';
`);
return results[0].rows.length > 0;
}
private async openDb(): Promise<SQLiteDatabase> {
return await SQLite.openDatabase({ ...this.dbParams });
}
async deleteDb(): Promise<void> {
await SQLite.deleteDatabase({ ...this.dbParams });
}
async executeSql(sql: string, params?: any[]): Promise<[ResultSet]> {
const db = await this.openDb();
try {
// https://github.com/andpor/react-native-sqlite-storage/issues/410
return await db.executeSql(sql, params);
} catch (err) {
try { await db.close(); } catch {}
throw err;
} finally {
try { await db.close(); } catch {}
}
}
async transaction(scope: (tx: Transaction) => void): Promise<void> {
const db = await this.openDb();
try {
await db.transaction(scope);
} catch (err) {
try { await db.close(); } catch {}
throw err;
} finally {
try { await db.close(); } catch {}
}
}
}

19
src/storage/music.ts Normal file
View File

@ -0,0 +1,19 @@
import { DbStorage } from './db';
export class MusicDb extends DbStorage {
constructor() {
super({ name: 'music.db', location: 'default' });
}
async createDb(): Promise<void> {
super.createDb(tx => {
tx.executeSql(`
CREATE TABLE artists (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
starred INTEGER NOT NULL
);
`);
});
}
}

103
src/storage/settings.ts Normal file
View File

@ -0,0 +1,103 @@
import { DbStorage } from './db';
export interface ServerSettings {
id: string;
address: string;
username: string;
token: string;
salt: string;
}
export interface AppSettings {
server?: string;
}
export class SettingsDb extends DbStorage {
constructor() {
super({ name: 'settings.db', location: 'Library' });
}
async createDb(): Promise<void> {
super.createDb(tx => {
tx.executeSql(`
CREATE TABLE servers (
id TEXT PRIMARY KEY NOT NULL,
address TEXT NOT NULL,
username TEXT NOT NULL,
token TEXT NOT NULL,
salt TEXT NOT NULL
);
`);
tx.executeSql(`
CREATE TABLE app (
server TEXT
);
`);
tx.executeSql(`
INSERT INTO app (server)
VALUES (NULL);
`);
});
}
async getServers(): Promise<ServerSettings[]> {
return (await this.executeSql(`
SELECT * FROM servers;
`))[0].rows.raw().map(x => ({
id: x.id,
address: x.address,
username: x.username,
token: x.token,
salt: x.salt,
}));
}
async getServer(id: string): Promise<ServerSettings | undefined> {
return (await this.getServers()).find(x => x.id === id);
}
async addServer(server: ServerSettings): Promise<void> {
await this.executeSql(`
INSERT INTO servers (id, address, username, token, salt)
VALUES (?, ?, ?, ?, ?);
`, [server.id, server.address, server.username, server.token, server.salt]);
}
async removeServer(id: string): Promise<void> {
await this.executeSql(`
DELETE FROM servers
WHERE id = ?;
`, [id]);
}
async updateServer(server: ServerSettings): Promise<void> {
await this.executeSql(`
UPDATE servers SET
address = ?,
username = ?,
token = ?,
salt = ?
WHERE id = ?;
`, [
server.address, server.username, server.token, server.salt,
server.id,
]);
}
async getApp(): Promise<AppSettings> {
return (await this.executeSql(`
SELECT * FROM app;
`))[0].rows.raw().map(x => ({
server: x.server || undefined,
}))[0];
}
async updateApp(app: AppSettings): Promise<void> {
await this.executeSql(`
UPDATE app SET
server = ?;
`, [
app.server || null,
]);
}
}