Spaces:
Running
Running
// Most of the code here is generate by intense AI promting. | |
// If you see any delulu let me know :) | |
import { Platform } from "react-native"; | |
import * as SQLite from 'expo-sqlite'; | |
import { ensure_safe_table_name } from "./ensure_safe_table_name"; | |
const DATABASE_NAME = 'ChapterDB'; | |
class Chapter_Storage_Web { | |
private static DATABASE_VERSION: number = 1; | |
private static async openDB(): Promise<IDBDatabase> { | |
return new Promise((resolve, reject) => { | |
const request = indexedDB.open(DATABASE_NAME, this.DATABASE_VERSION); | |
request.onupgradeneeded = (event) => { | |
const db = (event.target as IDBOpenDBRequest).result; | |
if (!db.objectStoreNames.contains('dataStore')) { | |
const store = db.createObjectStore('dataStore', { keyPath: 'id' }); | |
store.createIndex('item', 'item', { unique: false }); | |
store.createIndex('idx', 'idx', { unique: false }); | |
} | |
}; | |
request.onsuccess = () => { | |
resolve(request.result); | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async getAll(item: string, options?: { exclude_fields?: string[] }): Promise<any[]> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readonly'); | |
const store = transaction.objectStore('dataStore'); | |
const index = store.index('item'); | |
const request = index.openCursor(IDBKeyRange.only(item)); | |
const results: any[] = []; | |
request.onsuccess = (event) => { | |
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result; | |
if (cursor) { | |
const value = cursor.value; | |
if (options?.exclude_fields) { | |
options.exclude_fields.forEach(field => { | |
if (value.hasOwnProperty(field)) { | |
delete value[field]; | |
} | |
}); | |
} | |
results.push(value); | |
cursor.continue(); | |
} else { | |
resolve(results.sort((a, b) => a.idx - b.idx).reverse()); | |
} | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async get(item: string, id: string, options?: { exclude_fields?: string[] }): Promise<any | null> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readonly'); | |
const store = transaction.objectStore('dataStore'); | |
const request = store.get(id); | |
request.onsuccess = (event) => { | |
const result = (event.target as IDBRequest<any>).result; | |
if (result && result.item === item) { | |
if (options?.exclude_fields) { | |
options.exclude_fields.forEach(field => { | |
if (result.hasOwnProperty(field)) { | |
delete result[field]; | |
} | |
}); | |
} | |
resolve(result); | |
} else { | |
resolve(null); | |
} | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async getByIdx(item: string, idx: number, options?: { exclude_fields?: string[] }): Promise<any | null> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readonly'); | |
const store = transaction.objectStore('dataStore'); | |
const index = store.index('item'); // Assuming 'item' is the name of the index | |
const request = index.openCursor(IDBKeyRange.only(item)); | |
request.onsuccess = (event) => { | |
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result; | |
if (cursor) { | |
const record = cursor.value; | |
if (record.idx === idx) { | |
if (options?.exclude_fields) { | |
options.exclude_fields.forEach(field => { | |
if (record.hasOwnProperty(field)) { | |
delete record[field]; | |
} | |
}); | |
} | |
resolve(record); | |
return; | |
} | |
cursor.continue(); | |
} else { | |
resolve(null); | |
} | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async add(item: string, idx: number, id: string, title: string, data: any): Promise<void> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readwrite'); | |
const store = transaction.objectStore('dataStore'); | |
const request = store.add({ id, item, idx, title, data, data_state:"empty" }); | |
request.onsuccess = () => { | |
resolve(); | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async update(item: string, id: string, newData: any, data_state: string): Promise<void> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readwrite'); | |
const store = transaction.objectStore('dataStore'); | |
const index = store.index('item'); | |
const request = index.openCursor(IDBKeyRange.only(item)); | |
request.onsuccess = (event) => { | |
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result; | |
if (cursor) { | |
if (cursor.value.id === id) { | |
cursor.value.data = newData; | |
cursor.value.data_state = data_state; | |
const updateRequest = cursor.update(cursor.value); | |
updateRequest.onsuccess = () => { | |
resolve(); | |
}; | |
updateRequest.onerror = () => { | |
reject(updateRequest.error); | |
}; | |
} else { | |
cursor.continue(); | |
} | |
} else { | |
reject(new Error('Item not found')); | |
} | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async drop(item: string): Promise<void> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readwrite'); | |
const store = transaction.objectStore('dataStore'); | |
const index = store.index('item'); | |
const request = index.openCursor(IDBKeyRange.only(item)); | |
request.onsuccess = (event) => { | |
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result; | |
if (cursor) { | |
store.delete(cursor.primaryKey); | |
cursor.continue(); | |
} else { | |
resolve(); | |
} | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
public static async remove(item: string, id: string): Promise<void> { | |
const db = await this.openDB(); | |
return new Promise((resolve, reject) => { | |
const transaction = db.transaction('dataStore', 'readwrite'); | |
const store = transaction.objectStore('dataStore'); | |
const request = store.delete(id); | |
request.onsuccess = () => { | |
resolve(); | |
}; | |
request.onerror = () => { | |
reject(request.error); | |
}; | |
}); | |
} | |
} | |
class Chapter_Storage_Native { | |
private db:any; | |
constructor() { | |
this.initializeDatabase(); | |
} | |
private async initializeDatabase() { | |
if (!this.db) { | |
this.db = await SQLite.openDatabaseAsync(DATABASE_NAME); | |
} | |
} | |
async getAll(tableName: string, options: { exclude_fields: Array<string> }): Promise<any[]> { | |
await this.initializeDatabase(); | |
try { | |
const excludeFields = options?.exclude_fields; | |
if (excludeFields && excludeFields.length > 0) { | |
// Get all column names from the table | |
const columnsResult = await this.db!.getAllAsync(`PRAGMA table_info("${ensure_safe_table_name(tableName)}")`); | |
const columns = columnsResult.map((col: any) => col.name); | |
// Exclude the specified fields if provided | |
const selectedColumns = columns.filter((col: any) => !excludeFields.includes(col)); | |
// Construct the SELECT query with the selected columns | |
const query = `SELECT ${selectedColumns.join(', ')} FROM "${ensure_safe_table_name(tableName)}" ORDER BY idx`; | |
const allRows: Array<any> = await this.db!.getAllAsync(query); | |
allRows.forEach(row => {row.data = JSON.parse(row.data)}); | |
if (options?.exclude_fields && !options?.exclude_fields.includes("data")) allRows.forEach(row => {row.data = JSON.parse(row.data)}); | |
return allRows.sort((a, b) => a.idx - b.idx).reverse(); | |
} else { | |
// If no fields to exclude, select all columns | |
const allRows: Array<any> = await this.db!.getAllAsync(`SELECT * FROM "${ensure_safe_table_name(tableName)}" ORDER BY idx`); | |
return allRows.sort((a, b) => a.idx - b.idx).reverse(); | |
} | |
} catch (error) { | |
console.log("[Error] Chapter Storage Native (getAll): ",error) | |
return []; // Return empty array if table does not exist | |
} | |
} | |
async get(tableName: string, id: number, options?: { exclude_fields?: string[] }): Promise<any | null> { | |
await this.initializeDatabase(); | |
try { | |
const excludeFields = options?.exclude_fields; | |
if (excludeFields && excludeFields.length > 0) { | |
// Get all column names from the table | |
const columnsResult = await this.db!.getAllAsync(`PRAGMA table_info("${ensure_safe_table_name(tableName)}")`); | |
const columns = columnsResult.map((col: any) => col.name); | |
// Exclude the specified fields if provided | |
const selectedColumns = columns.filter((col: any) => !excludeFields.includes(col)); | |
// Construct the SELECT query with the selected columns | |
const query = `SELECT ${selectedColumns.join(', ')} FROM "${ensure_safe_table_name(tableName)}" WHERE id = ?`; | |
const firstRow = await this.db!.getFirstAsync(query, [id]); | |
if (!excludeFields.includes("data")) firstRow.data = JSON.parse(firstRow.data); | |
return firstRow || null; | |
} else { | |
// If no fields to exclude, select all columns | |
const firstRow = await this.db!.getFirstAsync(`SELECT * FROM "${ensure_safe_table_name(tableName)}" WHERE id = ?`, [id]); | |
firstRow.data = JSON.parse(firstRow.data); | |
return firstRow || null; | |
} | |
} catch (error) { | |
return null; // Return null if table or row does not exist | |
} | |
} | |
async getByIdx(tableName: string, idx: number, options?: { exclude_fields?: string[] }): Promise<any | null> { | |
await this.initializeDatabase(); | |
try { | |
const excludeFields = options?.exclude_fields; | |
if (excludeFields && excludeFields.length > 0) { | |
// Get all column names from the table | |
const columnsResult = await this.db!.getAllAsync(`PRAGMA table_info("${ensure_safe_table_name(tableName)}")`); | |
const columns = columnsResult.map((col: any) => col.name); | |
// Exclude the specified fields if provided | |
const selectedColumns = columns.filter((col: any) => !excludeFields.includes(col)); | |
// Construct the SELECT query with the selected columns | |
const query = `SELECT ${selectedColumns.join(', ')} FROM "${ensure_safe_table_name(tableName)}" WHERE idx = ?`; | |
const firstRow = await this.db!.getFirstAsync(query, [idx]); | |
if (!excludeFields.includes("data")) firstRow.data = JSON.parse(firstRow.data); | |
return firstRow || null; | |
} else { | |
// If no fields to exclude, select all columns | |
const firstRow = await this.db!.getFirstAsync(`SELECT * FROM "${ensure_safe_table_name(tableName)}" WHERE idx = ?`, [idx]); | |
firstRow.data = JSON.parse(firstRow.data); | |
return firstRow || null; | |
} | |
} catch (error) { | |
console.log("[Error] Chapter Storage Native (getByIdx): ",error) | |
return null; // Return null if table or row does not exist | |
} | |
} | |
async add(tableName: string, idx: number, id: string, title: string, data: string): Promise<void> { | |
await this.initializeDatabase(); | |
try { | |
await this.db!.runAsync(`CREATE TABLE IF NOT EXISTS "${ensure_safe_table_name(tableName)}" ( | |
idx INTEGER NOT NULL, | |
id TEXT PRIMARY KEY NOT NULL, | |
title TEXT NOT NULL, | |
data TEXT NOT NULL, | |
data_state TEXT NOT NULL | |
);`); | |
await this.db!.runAsync(`INSERT INTO "${ensure_safe_table_name(tableName)}" (idx, id, title, data, data_state) VALUES (?, ?, ?, ?, ?)`, [idx, id, title, JSON.stringify(data), "empty"]); | |
} catch (error:any) { | |
console.log(error.message) | |
throw new Error(`Failed to add data: ${error.message}`); | |
} | |
} | |
async update(tableName: string, id: string, data: string, data_state: string): Promise<void> { | |
await this.initializeDatabase(); | |
try { | |
const query = `UPDATE "${ensure_safe_table_name(tableName)}" SET data = ?, data_state = ? WHERE id = ?`; | |
await this.db!.runAsync(query, [JSON.stringify(data), data_state, id]); | |
console.log(query, [JSON.stringify(data), data_state, id]); | |
} catch (error: any) { | |
console.log(error.message); | |
throw new Error(`Failed to update data: ${error.message}`); | |
} | |
} | |
async remove(tableName: string, id: number): Promise<void> { | |
await this.initializeDatabase(); | |
try { | |
await this.db!.runAsync(`DELETE FROM "${ensure_safe_table_name(tableName)}" WHERE id = ?`, [id]); | |
} catch (error:any) { | |
throw new Error(`Failed to remove data: ${error.message}`); | |
} | |
} | |
async drop(tableName: string): Promise<void> { | |
await this.initializeDatabase(); | |
try { | |
await this.db!.execAsync(`DROP TABLE IF EXISTS "${ensure_safe_table_name(tableName)}"`); | |
} catch (error:any) { | |
throw new Error(`Failed to drop table: ${error.message}`); | |
} | |
} | |
} | |
var ChapterStorage:any | |
if (Platform.OS === 'web') { | |
ChapterStorage = Chapter_Storage_Web | |
}else{ | |
ChapterStorage = new Chapter_Storage_Native() | |
} | |
export default ChapterStorage; | |
/* | |
[item] = "source-comic_id" | |
- Add data with different items and indexes | |
await ChapterStorage.add('itemA', 1<index>, 'id-123', 'TitleA', { key: 'valueA' }); | |
await ChapterStorage.add('itemB', 2, 'id-456', 'TitleB', { key: 'valueB' }); | |
await ChapterStorage.add('itemA', 3, 'id-789', 'TitleC', { key: 'valueC' }); | |
- Get all data by item, sorted by index | |
const dataItemA = await ChapterStorage.getAll('itemA'); | |
const dataItemB = await ChapterStorage.getAll('itemB'); | |
- Get a single data item by item and id | |
const singleDataItemA = await ChapterStorage.get('itemA', 'id-123'); | |
const singleDataItemB = await ChapterStorage.get('itemB', 'id-456'); | |
- Drop all data with a specific item | |
await ChapterStorage.drop('itemA'); | |
- Remove data by item and id | |
await ChapterStorage.remove('itemB', 'id-456'); | |
*/ | |