// Dart imports: import 'dart:io'; // Package imports: import 'package:sqflite/sqflite.dart'; // Project imports: import 'package:openlib/services/files.dart'; class MyBook { final String id; final String title; final String? author; final String? thumbnail; final String link; final String? publisher; final String? info; final String? description; final String? format; MyBook( {required this.id, required this.title, required this.author, required this.thumbnail, required this.link, required this.publisher, required this.info, required this.format, required this.description}); Map toMap() { return { 'id': id, 'title': title, 'author': author, 'thumbnail': thumbnail, 'link': link, 'publisher': publisher, 'info': info, 'format': format, 'description': description }; } @override String toString() { return 'MyBook{id: $id,title: $title,author: $author,thumbnail: $thumbnail,link: $link,publisher: $publisher,info: $info,format: $format,description:$description}'; } } class MyLibraryDb { static final MyLibraryDb instance = MyLibraryDb._internal(); static Database? _database; MyLibraryDb._internal(); Future get database async { if (_database != null) { return _database!; } _database = await _initDatabase(); return _database!; } Future _initDatabase() async { final databasePath = await getDatabasesPath(); final path = '$databasePath/mylibrary.db'; final bool isMobile = Platform.isAndroid || Platform.isIOS; return await openDatabase( path, version: 5, onCreate: (Database db, int version) async { await db.execute( 'CREATE TABLE mybooks (id TEXT PRIMARY KEY, title TEXT,author TEXT,thumbnail TEXT,link TEXT,publisher TEXT,info TEXT,format TEXT,description TEXT)'); await db.execute( 'CREATE TABLE preferences (name TEXT PRIMARY KEY,value TEXT)'); if (isMobile || true) { // TODO: Breaks getBrowserOptions() on Mac await db.execute( 'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)'); await db.execute( 'CREATE TABLE browserOptions (name TEXT PRIMARY KEY,value TEXT)'); } }, onUpgrade: (db, oldVersion, newVersion) async { List isTableExist = await db.query('sqlite_master', where: 'name = ?', whereArgs: ['bookposition']); List isPreferenceTableExist = await db.query('sqlite_master', where: 'name = ?', whereArgs: ['preferences']); List isbrowserOptionsExist = await db.query('sqlite_master', where: 'name = ?', whereArgs: ['browserOptions']); if (isPreferenceTableExist.isEmpty) { await db.execute( 'CREATE TABLE preferences (name TEXT PRIMARY KEY,value TEXT)'); } if (isMobile && isTableExist.isEmpty) { await db.execute( 'CREATE TABLE bookposition (fileName TEXT PRIMARY KEY,position TEXT)'); } if (isMobile && isbrowserOptionsExist.isEmpty) { await db.execute( 'CREATE TABLE browserOptions (name TEXT PRIMARY KEY,value TEXT)'); } }, onOpen: (db) async { final bookStorageDefaultDirectory = await getBookStorageDefaultDirectory; await db.execute( "INSERT OR IGNORE INTO preferences (name, value) VALUES ('darkMode', 0)"); await db.execute( "INSERT OR IGNORE INTO preferences (name, value) VALUES ('openPdfwithExternalApp', 0)"); await db.execute( "INSERT OR IGNORE INTO preferences (name, value) VALUES ('openEpubwithExternalApp', 0)"); await db.execute( "INSERT OR IGNORE INTO preferences (name, value) VALUES ('bookStorageDirectory', '$bookStorageDefaultDirectory')"); }, ); } // Database dbInstance; String tableName = 'mybooks'; Future insert(MyBook book) async { final dbInstance = await instance.database; await dbInstance.insert( tableName, book.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } Future delete(String id) async { final dbInstance = await instance.database; await dbInstance.delete( tableName, where: 'id = ?', whereArgs: [id], ); } Future getId(String id) async { final dbInstance = await instance.database; List> data = await dbInstance.query(tableName, where: 'id = ?', whereArgs: [id]); List book = listMapToMyBook(data); if (book.isNotEmpty) { return book.first; } return null; } Future checkIdExists(String id) async { final dbInstance = await instance.database; List> data = await dbInstance.query(tableName, where: 'id = ?', whereArgs: [id]); List book = listMapToMyBook(data); if (book.isNotEmpty) { return true; } return false; } Future> getAll() async { final dbInstance = await instance.database; final List> maps = await dbInstance.query(tableName); return listMapToMyBook(maps); } List listMapToMyBook(List> maps) { List myBookList = List.generate(maps.length, (i) { return MyBook( id: maps[i]['id'], title: maps[i]['title'], author: maps[i]['author'], thumbnail: maps[i]['thumbnail'], link: maps[i]['link'], publisher: maps[i]['publisher'], info: maps[i]['info'], format: maps[i]['format'], description: maps[i]['description']); }); return myBookList.reversed.toList(); } Future saveBookState(String fileName, String position) async { final dbInstance = await instance.database; await dbInstance.insert( 'bookposition', {'fileName': fileName, 'position': position}, conflictAlgorithm: ConflictAlgorithm.replace, ); } Future deleteBookState(String fileName) async { final dbInstance = await instance.database; await dbInstance.delete( 'bookposition', where: 'fileName = ?', whereArgs: [fileName], ); } Future getBookState(String fileName) async { final dbInstance = await instance.database; List> data = await dbInstance .query('bookposition', where: 'fileName = ?', whereArgs: [fileName]); List dataList = List.generate(data.length, (i) { return {'fileName': data[i]['fileName'], 'position': data[i]['position']}; }); if (dataList.isNotEmpty) { return dataList[0]['position']; } else { return null; } } Future savePreference(String name, dynamic value) async { switch (value.runtimeType) { case bool: value = value ? 1 : 0; break; case int || String: break; default: throw 'Invalid type'; } Database dbInstance = await instance.database; await dbInstance.insert( 'preferences', {'name': name, 'value': value}, conflictAlgorithm: ConflictAlgorithm.replace, ); } Future getPreference(String name) async { Database dbInstance = await instance.database; List> data = await dbInstance .query('preferences', where: 'name = ?', whereArgs: [name]); List dataList = List.generate(data.length, (i) { return {'name': data[i]['name'], 'value': data[i]['value']}; }); if (dataList.isNotEmpty) { // Convert to int if possible int? preference = int.tryParse(dataList[0]['value']); if (preference != null) { return preference; } // Return string value if not int return dataList[0]['value']; } throw "Preference $name not found"; } Future setBrowserOptions(String name, String value) async { final dbInstance = await instance.database; await dbInstance.insert( 'browserOptions', {'name': name, 'value': value}, conflictAlgorithm: ConflictAlgorithm.replace, ); } Future getBrowserOptions(String name) async { final dbInstance = await instance.database; List> data = await dbInstance .query('browserOptions', where: 'name = ?', whereArgs: [name]); List dataList = List.generate(data.length, (i) { return {'name': data[i]['name'], 'value': data[i]['value']}; }); if (dataList.isNotEmpty) { return dataList[0]['value']; } else { return ""; } } }