Files
BadNote/lib/services/vault_service.dart

71 lines
2.5 KiB
Dart
Raw Normal View History

import 'dart:io';
import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:shared_preferences/shared_preferences.dart';
/// Records the user-picked vault root folder (an Obsidian-style vault) and
/// gates app startup behind a valid choice.
///
/// The vault root is the single folder under which all notebooks will live.
/// Phase 0 only persists the path and validates it exists; no data is moved
/// into the vault yet (later phases do that).
///
/// Persistence is SharedPreferences-backed under [vaultRootKey]. The service is
/// easily mockable: inject a [SharedPreferences] (e.g. from
/// `SharedPreferences.setMockInitialValues`) via the constructor for tests.
class VaultService {
/// SharedPreferences key under which the vault root path is stored.
static const String vaultRootKey = 'vaultRoot';
final SharedPreferences _prefs;
VaultService._(this._prefs);
static VaultService? _instance;
/// Singleton accessor, mirroring [DatabaseService.getInstance]. Lazily reads
/// the shared [SharedPreferences] instance.
static Future<VaultService> getInstance() async {
if (_instance != null) return _instance!;
final prefs = await SharedPreferences.getInstance();
final service = VaultService._(prefs);
_instance = service;
return service;
}
/// Test-only constructor: inject a (typically mock) [SharedPreferences] so
/// the vault root can be exercised without platform channels.
@visibleForTesting
VaultService.forTest(SharedPreferences prefs) : _prefs = prefs;
/// Test-only: drop the cached singleton so the next [getInstance] rebuilds.
@visibleForTesting
static void resetForTest() {
_instance = null;
}
/// The currently stored vault root path, or null if none has been chosen.
String? get vaultRoot => _prefs.getString(vaultRootKey);
/// Persist [path] as the vault root.
Future<void> setVaultRoot(String path) async {
await _prefs.setString(vaultRootKey, path);
}
/// Forget the stored vault root (e.g. to re-prompt the user).
Future<void> clearVaultRoot() async {
await _prefs.remove(vaultRootKey);
}
/// True iff a vault root is set AND that directory currently exists.
///
/// Returns false when no path is stored or when the stored path no longer
/// resolves to a directory (external drive unplugged, folder deleted) — the
/// caller then re-prompts rather than silently scattering data elsewhere.
Future<bool> vaultRootValid() async {
final path = vaultRoot;
if (path == null || path.isEmpty) return false;
return Directory(path).exists();
}
}