import 'dart:async'; import 'package:drift/drift.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../data/database.dart'; import '../services/cbz_store.dart'; import '../services/download_queue.dart'; import '../services/image_headers.dart'; import '../services/ntfy_notifier.dart'; import '../services/peek_mode.dart'; import '../services/reader_direction.dart'; import '../services/webdav_sync.dart'; import '../sources/registry.dart'; final sourceRegistryProvider = Provider((ref) { return SourceRegistry(); }); final databaseProvider = Provider((ref) { final db = AppDatabase(); ref.onDispose(db.close); return db; }); final cbzStoreProvider = Provider((ref) => CbzStore()); final ntfyNotifierProvider = Provider((ref) { return NtfyNotifier(store: ref.watch(cbzStoreProvider)); }); final downloadQueueProvider = ChangeNotifierProvider((ref) { final queue = DownloadQueue( db: ref.watch(databaseProvider), sources: ref.watch(sourceRegistryProvider), store: ref.watch(cbzStoreProvider), ntfy: ref.watch(ntfyNotifierProvider), ); // Clear leftover queue rows for chapters already on disk (e.g. older builds). unawaited(queue.reconcileStaleJobs()); return queue; }); /// Per-request headers come from [ComicCover] / reader via `httpHeaders`. final imageCacheManagerProvider = Provider((ref) { return HeaderCacheManager(const {}); }); final libraryTitlesProvider = StreamProvider>((ref) { final db = ref.watch(databaseProvider); return (db.select(db.titles) ..where((t) => t.inLibrary.equals(true)) ..orderBy([(t) => OrderingTerm.asc(t.title)])) .watch(); }); final historyTitlesProvider = StreamProvider>((ref) { final db = ref.watch(databaseProvider); return (db.select(db.titles) ..where((t) => t.lastReadAt.isNotNull()) ..orderBy([(t) => OrderingTerm.desc(t.lastReadAt)])) .watch(); }); final queueJobsProvider = StreamProvider>((ref) { final db = ref.watch(databaseProvider); return db.watchQueueJobs(); }); final downloadedSeriesProvider = StreamProvider>((ref) { final db = ref.watch(databaseProvider); return db.watchDownloadedSeries(); }); class PeekModeNotifier extends StateNotifier { PeekModeNotifier() : super(false) { _load(); } Future _load() async { state = await PeekMode.load(); } Future toggle() async { state = !state; await PeekMode.save(state); } Future setEnabled(bool enabled) async { state = enabled; await PeekMode.save(enabled); } } final peekModeProvider = StateNotifierProvider((ref) => PeekModeNotifier()); class ReadingDirectionNotifier extends StateNotifier { ReadingDirectionNotifier() : super(ReadingDirection.ltr) { _load(); } Future _load() async { state = await ReaderDirection.load(); } Future setDirection(ReadingDirection direction) async { state = direction; await ReaderDirection.save(direction); } Future toggle() async { await setDirection( state == ReadingDirection.ltr ? ReadingDirection.rtl : ReadingDirection.ltr, ); } } final readingDirectionProvider = StateNotifierProvider( (ref) => ReadingDirectionNotifier(), ); final webDavSyncProvider = Provider((ref) { return WebDavSyncService(ref.watch(databaseProvider)); });