mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 23:02:43 +01:00
first interactions with db (state cache)
now we're cookin' with custom hooks
This commit is contained in:
53
src/components/ArtistsList.tsx
Normal file
53
src/components/ArtistsList.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { Button, FlatList, Text, View } from 'react-native';
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { artistsState, ArtistState, useUpdateArtists } from '../state/artists';
|
||||
|
||||
const ArtistItem: React.FC<{ item: ArtistState } > = ({ item }) => (
|
||||
<View>
|
||||
<Text>{item.id}</Text>
|
||||
<Text style={{
|
||||
fontSize: 60,
|
||||
paddingBottom: 400,
|
||||
}}>{item.name}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const List = () => {
|
||||
const artists = useRecoilValue(artistsState);
|
||||
|
||||
const renderItem: React.FC<{ item: ArtistState }> = ({ item }) => (
|
||||
<ArtistItem item={item} />
|
||||
);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={artists}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const ArtistsList = () => {
|
||||
const resetArtists = useResetRecoilState(artistsState);
|
||||
const updateArtists = useUpdateArtists();
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Button
|
||||
title='Reset to default'
|
||||
onPress={resetArtists}
|
||||
/>
|
||||
<Button
|
||||
title='Update from API'
|
||||
onPress={updateArtists}
|
||||
/>
|
||||
<React.Suspense fallback={<Text>Loading...</Text>}>
|
||||
<List />
|
||||
</React.Suspense>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArtistsList;
|
||||
67
src/db/client.ts
Normal file
67
src/db/client.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
65
src/state/artists.ts
Normal file
65
src/state/artists.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import md5 from 'md5';
|
||||
import { atom, selector, useSetRecoilState } from 'recoil';
|
||||
import { SubsonicApiClient } from '../subsonic/client';
|
||||
import { MusicDb } from '../db/client';
|
||||
|
||||
const db = new MusicDb();
|
||||
|
||||
const password = 'test';
|
||||
const salt = 'salty';
|
||||
const token = md5(password + salt);
|
||||
|
||||
const client = new SubsonicApiClient('http://navidrome.home', 'austin', token, salt);
|
||||
|
||||
export interface ArtistState {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const artistsState = atom<ArtistState[]>({
|
||||
key: 'artistsState',
|
||||
default: selector({
|
||||
key: 'artistsState/default',
|
||||
get: async () => {
|
||||
await prepopulate();
|
||||
|
||||
const results = await db.executeSql(`
|
||||
SELECT * FROM artists;
|
||||
`);
|
||||
|
||||
return results[0].rows.raw().map(i => ({
|
||||
id: i.id,
|
||||
name: i.name,
|
||||
}));
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export const useUpdateArtists = () => {
|
||||
const setArtists = useSetRecoilState(artistsState);
|
||||
return async () => {
|
||||
const response = await client.getArtists();
|
||||
|
||||
setArtists(response.data.artists.map(i => ({
|
||||
id: i.id,
|
||||
name: i.name,
|
||||
})));
|
||||
};
|
||||
};
|
||||
|
||||
async function prepopulate() {
|
||||
try { await db.deleteDb() } catch {}
|
||||
await db.createDb();
|
||||
await db.executeSql(`
|
||||
INSERT INTO artists (id, name, starred)
|
||||
VALUES (?, ?, ?);
|
||||
`,
|
||||
[1, 'good guy', true]
|
||||
);
|
||||
await db.executeSql(`
|
||||
INSERT INTO artists (id, name, starred)
|
||||
VALUES (?, ?, ?);
|
||||
`,
|
||||
[2, 'bad guy', false]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user