mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 19:27:01 +01:00
First step of moving the desktop app off Flutter's Linux embedder, which tops
out at ~20fps at 4K (see docs/desktop-performance-findings.md). Android is
unaffected and keeps every existing code path.
The blocker was that `dart:io` was imported by note_repository, capture_screen
and settings_screen. On web it compiles to a stub and then throws at runtime --
the first web build rendered a blank white window. So each `dart:io` use now
sits behind a conditional export, with the shared logic hoisted into pure Dart:
- note_repository loses `dart:io`/sqlite entirely. Platform wiring moves to
repository_factory{,_io,_web}, and the legacy sqlite import moves to its own
io-only library. The HLC-from-updated_at seeding is preserved verbatim, so
independently-migrating devices still resolve pre-cutover edits by real edit
time.
- Notes persist to IndexedDB on web, not localStorage: the log is the primary
copy of the backlog and localStorage caps at ~5-10MB and is evicted eagerly.
- BACKLOG.md and the explicit Export keep working through a local wrapper
(added next). A browser cannot write `~/todo/BACKLOG.md`, and the user's
tooling plus the todo MCP server read that path, so leaving it to rot was not
an option. Writes are best-effort so the app still works with no wrapper.
- Notes are also mirrored to the wrapper's on-disk copy, so a wiped Chrome
profile is not a total-loss event for notes that have not reached GitHub yet.
`openRepository` is split so `openRepositoryIn` keeps the migration tests; the
suite stays at 174 green and 100% coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
2.4 KiB
Dart
65 lines
2.4 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:crdt_sync/crdt_sync_io.dart';
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
|
import 'package:todo/data/legacy_sqlite_migration.dart';
|
|
import 'package:todo/data/note_repository.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
/// Opens the note store on a `dart:io` platform (Android).
|
|
///
|
|
/// Also performs the one-time, idempotent import of a pre-crdt_sync sqlite
|
|
/// database, guarded by a persisted flag. The legacy DB file is left untouched
|
|
/// as a safety net.
|
|
// coverage:ignore-start
|
|
// Resolves the real per-platform application-support directory, so it cannot
|
|
// run under test; [openRepositoryIn] holds all the logic and is covered.
|
|
Future<NoteRepository> openRepository() async {
|
|
final dir = await getApplicationSupportDirectory();
|
|
return openRepositoryIn(dir.path);
|
|
}
|
|
// coverage:ignore-end
|
|
|
|
/// Opens the note store rooted at [dirPath].
|
|
///
|
|
/// Split out from [openRepository] so tests can drive the legacy-migration
|
|
/// paths against a temporary directory without going through `path_provider`.
|
|
Future<NoteRepository> openRepositoryIn(String dirPath) async {
|
|
// Keep the FFI initialisation for any non-Android io host that still reads a
|
|
// legacy DB (and for the test host, which is Linux).
|
|
if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
|
sqfliteFfiInit();
|
|
}
|
|
|
|
final dbPath = p.join(dirPath, 'todo.db');
|
|
final persistence = FileLogPersistence(
|
|
File(p.join(dirPath, NoteRepository.logFileName)),
|
|
);
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final migrated = prefs.getBool(NoteRepository.kMigrated) ?? false;
|
|
var nodeId = prefs.getString(NoteRepository.kNodeId) ?? '';
|
|
|
|
if (!migrated && LegacySqliteMigration.exists(dbPath)) {
|
|
final legacy = await LegacySqliteMigration.read(dbPath, nodeId);
|
|
nodeId = legacy.nodeId;
|
|
final repository = await NoteRepository.openWith(
|
|
persistence: persistence,
|
|
nodeId: nodeId,
|
|
);
|
|
await repository.seedFrom(legacy.log);
|
|
await prefs.setString(NoteRepository.kNodeId, nodeId);
|
|
await prefs.setBool(NoteRepository.kMigrated, true);
|
|
return repository;
|
|
}
|
|
|
|
if (nodeId.isEmpty) {
|
|
nodeId = const Uuid().v4();
|
|
await prefs.setString(NoteRepository.kNodeId, nodeId);
|
|
}
|
|
return NoteRepository.openWith(persistence: persistence, nodeId: nodeId);
|
|
}
|