feat: navigation store feat: implement OPDS entry caching system Add comprehensive caching for OPDS publications with local image storage and Dublin Core Terms metadata extraction. This enables offline viewing, fixes Library screen to show book titles and covers, and improves overall UX. - Add publications table with full metadata (title, authors, series, publisher, ISBN, language) - Implement local image cache service for covers and thumbnails - Extract DCTerms metadata (series, publisher, language, ISBN) from OPDS feeds - Link reading progress to cached publications - Update Library screen to display cached publication data and covers - Cache publications automatically when starting to read - Fix navigation stack issues by using context.push() instead of context.go() - Add explicit back button to EPUB reader with proper PopScope handling - Implement UNIQUE constraint on reading_progress to prevent duplicates - Save reading progress on screen dispose for both EPUB and image readers Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
86 lines
2.8 KiB
Dart
86 lines
2.8 KiB
Dart
import 'package:sqflite/sqflite.dart';
|
|
import 'package:path/path.dart';
|
|
import 'package:worldhopper/config/constants.dart';
|
|
import 'package:worldhopper/database/tables/servers_table.dart';
|
|
import 'package:worldhopper/database/tables/reading_progress_table.dart';
|
|
import 'package:worldhopper/database/tables/publications_table.dart';
|
|
|
|
/// Database helper for SQLite operations
|
|
class DatabaseHelper {
|
|
DatabaseHelper._();
|
|
|
|
static final DatabaseHelper instance = DatabaseHelper._();
|
|
static Database? _database;
|
|
|
|
/// Get the database instance, creating it if necessary
|
|
Future<Database> get database async {
|
|
if (_database != null) return _database!;
|
|
_database = await _initDatabase();
|
|
return _database!;
|
|
}
|
|
|
|
/// Initialize the database
|
|
Future<Database> _initDatabase() async {
|
|
final databasePath = await getDatabasesPath();
|
|
final path = join(databasePath, AppConstants.databaseName);
|
|
|
|
return await openDatabase(
|
|
path,
|
|
version: AppConstants.databaseVersion,
|
|
onCreate: _onCreate,
|
|
onUpgrade: _onUpgrade,
|
|
onConfigure: _onConfigure,
|
|
);
|
|
}
|
|
|
|
/// Configure database settings
|
|
Future<void> _onConfigure(Database db) async {
|
|
// Enable foreign key constraints
|
|
await db.execute('PRAGMA foreign_keys = ON');
|
|
}
|
|
|
|
/// Create database tables
|
|
Future<void> _onCreate(Database db, int version) async {
|
|
// Create servers table
|
|
await db.execute(ServersTable.createTable);
|
|
|
|
// Create publications table
|
|
await db.execute(PublicationsTable.createTable);
|
|
await db.execute(PublicationsTable.createServerIndex);
|
|
await db.execute(PublicationsTable.createServerOpdsIndex);
|
|
await db.execute(PublicationsTable.createAccessedIndex);
|
|
|
|
// Create reading_progress table
|
|
await db.execute(ReadingProgressTable.createTable);
|
|
await db.execute(ReadingProgressTable.createPublicationIndex);
|
|
await db.execute(ReadingProgressTable.createServerIndex);
|
|
await db.execute(ReadingProgressTable.createCacheIdIndex);
|
|
}
|
|
|
|
/// Handle database upgrades
|
|
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
|
// No backwards compatibility needed - just recreate the database
|
|
// Drop all existing tables
|
|
await db.execute(ReadingProgressTable.dropTable);
|
|
await db.execute(PublicationsTable.dropTable);
|
|
await db.execute(ServersTable.dropTable);
|
|
|
|
// Recreate with new schema
|
|
await _onCreate(db, newVersion);
|
|
}
|
|
|
|
/// Close the database
|
|
Future<void> close() async {
|
|
final db = await database;
|
|
await db.close();
|
|
_database = null;
|
|
}
|
|
|
|
/// Delete the database (for testing purposes)
|
|
Future<void> deleteDatabase() async {
|
|
final databasePath = await getDatabasesPath();
|
|
final path = join(databasePath, AppConstants.databaseName);
|
|
await databaseFactory.deleteDatabase(path);
|
|
_database = null;
|
|
}
|
|
}
|