ComicMTL / frontend /constants /module /chapter_storage.tsx
BloodyInside's picture
firsty
947c08e
raw
history blame
15.3 kB
// 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');
*/