feat: add local-first private AI digest workflow

Migrate app code into canonical feature slices, add phone-only AI digest scheduling and review, wire local notification/background task support, and cover the flow with tests.
This commit is contained in:
Rijad Zuzo
2026-05-17 00:17:20 +02:00
parent dab50abf0e
commit f655adfbea
212 changed files with 24178 additions and 15895 deletions
+4
View File
@@ -0,0 +1,4 @@
# Legacy Local Storage Exports
These files exist as compatibility exports while the canonical storage layer has
moved to `lib/app/data/storage/`. Prefer editing the app-layer storage files.
@@ -1,19 +1,2 @@
/// Raw local state payload and associated schema version.
class LocalDataRecord {
const LocalDataRecord({required this.rawState, required this.schemaVersion});
final String rawState;
final int schemaVersion;
}
/// Persistence boundary for local app state.
abstract interface class LocalDataStore {
/// Reads latest persisted state payload.
Future<LocalDataRecord?> read();
/// Writes state payload and schema version.
Future<void> write({required String rawState, required int schemaVersion});
/// Clears persisted state payload.
Future<void> clear();
}
// Legacy compatibility export for the app storage contract.
export 'package:relationship_saver/app/data/storage/local_data_store.dart';
@@ -1,54 +1,2 @@
import 'package:hive_flutter/hive_flutter.dart';
import 'package:relationship_saver/features/local/storage/local_data_store.dart';
/// Hive-backed local store for future DB migration path.
class HiveLocalDataStore implements LocalDataStore {
static const String _boxName = 'relationship_saver_local';
static const String _stateKey = 'state_json';
static const String _schemaVersionKey = 'schema_version';
static bool _initialized = false;
@override
Future<void> clear() async {
final Box<dynamic> box = await _openBox();
await box.delete(_stateKey);
await box.delete(_schemaVersionKey);
}
@override
Future<LocalDataRecord?> read() async {
final Box<dynamic> box = await _openBox();
final String? rawState = box.get(_stateKey) as String?;
if (rawState == null || rawState.isEmpty) {
return null;
}
final int schemaVersion =
(box.get(_schemaVersionKey) as num?)?.toInt() ?? 0;
return LocalDataRecord(rawState: rawState, schemaVersion: schemaVersion);
}
@override
Future<void> write({
required String rawState,
required int schemaVersion,
}) async {
final Box<dynamic> box = await _openBox();
await box.put(_stateKey, rawState);
await box.put(_schemaVersionKey, schemaVersion);
}
Future<Box<dynamic>> _openBox() async {
if (!_initialized) {
await Hive.initFlutter();
_initialized = true;
}
if (!Hive.isBoxOpen(_boxName)) {
return Hive.openBox<dynamic>(_boxName);
}
return Hive.box<dynamic>(_boxName);
}
}
// Legacy compatibility export for the Hive local data store.
export 'package:relationship_saver/app/data/storage/local_data_store_hive.dart';
@@ -1,22 +1,2 @@
import 'package:relationship_saver/features/local/storage/local_data_store.dart';
/// In-memory local store for deterministic tests.
class InMemoryLocalDataStore implements LocalDataStore {
LocalDataRecord? _record;
@override
Future<void> clear() async {
_record = null;
}
@override
Future<LocalDataRecord?> read() async => _record;
@override
Future<void> write({
required String rawState,
required int schemaVersion,
}) async {
_record = LocalDataRecord(rawState: rawState, schemaVersion: schemaVersion);
}
}
// Legacy compatibility export for the in-memory local data store.
export 'package:relationship_saver/app/data/storage/local_data_store_in_memory.dart';
@@ -1,14 +1,2 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:relationship_saver/core/config/app_config.dart';
import 'package:relationship_saver/features/local/storage/local_data_store.dart';
import 'package:relationship_saver/features/local/storage/local_data_store_hive.dart';
import 'package:relationship_saver/features/local/storage/local_data_store_shared_prefs.dart';
/// Chooses local persistence backend for app state.
final Provider<LocalDataStore> localDataStoreProvider =
Provider<LocalDataStore>((Ref ref) {
if (AppConfig.useHiveLocalDb) {
return HiveLocalDataStore();
}
return SharedPrefsLocalDataStore();
});
// Legacy compatibility export for the local data store provider.
export 'package:relationship_saver/app/data/storage/local_data_store_provider.dart';
@@ -1,44 +1,2 @@
import 'package:relationship_saver/features/local/storage/local_data_store.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// shared_preferences-backed local store used as stable default.
class SharedPrefsLocalDataStore implements LocalDataStore {
SharedPrefsLocalDataStore({
this.stateKey = 'local_data_state_v1',
this.schemaVersionKey = 'local_data_schema_version',
});
final String stateKey;
final String schemaVersionKey;
@override
Future<void> clear() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove(stateKey);
await prefs.remove(schemaVersionKey);
}
@override
Future<LocalDataRecord?> read() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String? rawState = prefs.getString(stateKey);
if (rawState == null || rawState.isEmpty) {
return null;
}
return LocalDataRecord(
rawState: rawState,
schemaVersion: prefs.getInt(schemaVersionKey) ?? 0,
);
}
@override
Future<void> write({
required String rawState,
required int schemaVersion,
}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(stateKey, rawState);
await prefs.setInt(schemaVersionKey, schemaVersion);
}
}
// Legacy compatibility export for the shared preferences local store.
export 'package:relationship_saver/app/data/storage/local_data_store_shared_prefs.dart';