feat(flutter): add cache initialization wiring and avatar download shims

- Add wireCache() to app_bootstrap using getApplicationCacheDirectory()
- Call wireCache() after wireStorage() in main bootstrap flow
- Add Dart-side initCache and downloadAvatar wrapper shims in api.dart
- Update Cargo.lock for new chanora_cache dependency
This commit is contained in:
Edison Jwa
2026-06-10 07:09:55 +09:00
parent 1fa9940e88
commit 8ffd33b3cc
4 changed files with 438 additions and 16 deletions
+4 -1
View File
@@ -93,7 +93,10 @@ String _kAppVersion = appSemverBaseline;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await RustLib.init();
unawaited(wireStorage());
unawaited(() async {
await wireStorage();
await wireCache();
}());
unawaited(wireConnectivity());
wireAudioLifecycle();
await configureBundledVadModels();
@@ -15,15 +15,22 @@ typedef StorageDirectoryProvider = Future<Directory> Function();
typedef StorageInitializer = Future<void> Function(String dir);
Future<void>? _storageInitFuture;
Future<void>? _cacheInitFuture;
Future<void>? _vadBootstrapFuture;
StorageDirectoryProvider _storageDirectoryProvider =
getApplicationSupportDirectory;
StorageDirectoryProvider _cacheDirectoryProvider = getApplicationCacheDirectory;
StorageInitializer _storageInitializer = _defaultStorageInitializer;
StorageInitializer _cacheInitializer = _defaultCacheInitializer;
Future<void> _defaultStorageInitializer(String dir) {
return rust.initStorage(dir: dir);
}
Future<void> _defaultCacheInitializer(String dir) {
return rust.initCache(dir: dir);
}
Future<File> _copyBundledAssetToDocuments({
required String assetPath,
required String fileName,
@@ -121,16 +128,50 @@ Future<void> _wireStorageImpl() async {
}
}
Future<void> wireCache() async {
final existing = _cacheInitFuture;
if (existing != null) {
await existing;
return;
}
final initFuture = _wireCacheImpl();
_cacheInitFuture = initFuture;
await initFuture;
}
Future<void> _wireCacheImpl() async {
var initialized = false;
try {
final dir = await _cacheDirectoryProvider();
await _cacheInitializer(dir.path);
initialized = true;
} catch (_) {
// Best-effort; missing cache just means protocol-owned assets are
// re-downloaded this session.
} finally {
if (!initialized) {
_cacheInitFuture = null;
}
}
}
@visibleForTesting
void debugResetStorageBootstrap({
StorageDirectoryProvider? storageDirectoryProvider,
StorageDirectoryProvider? cacheDirectoryProvider,
StorageInitializer? storageInitializer,
StorageInitializer? cacheInitializer,
}) {
_storageInitFuture = null;
_cacheInitFuture = null;
_vadBootstrapFuture = null;
_storageDirectoryProvider =
storageDirectoryProvider ?? getApplicationSupportDirectory;
_cacheDirectoryProvider =
cacheDirectoryProvider ?? getApplicationCacheDirectory;
_storageInitializer = storageInitializer ?? _defaultStorageInitializer;
_cacheInitializer = cacheInitializer ?? _defaultCacheInitializer;
}
rust.BridgeNetworkState _mapConnectivity(List<ConnectivityResult> results) {
@@ -226,6 +226,32 @@ String exportDiagnostics() => RustLib.instance.api.crateApiExportDiagnostics();
Future<void> initStorage({required String dir}) =>
RustLib.instance.api.crateApiInitStorage(dir: dir);
/// Wire the blob cache to a platform-private cache directory.
Future<void> initCache({required String dir}) {
final api = RustLib.instance.api as dynamic;
try {
return api.crateApiInitCache(dir: dir) as Future<void>;
} on NoSuchMethodError {
return Future.value();
}
}
/// Return an avatar from cache when present; otherwise download it.
Future<List<int>?> downloadAvatar({
required String avatarHash,
required String clientUid,
}) {
final api = RustLib.instance.api as dynamic;
try {
return api.crateApiDownloadAvatar(
avatarHash: avatarHash,
clientUid: clientUid,
) as Future<List<int>?>;
} on NoSuchMethodError {
return Future.value();
}
}
/// List persisted bookmarks.
Future<List<BridgeBookmark>> listBookmarks() =>
RustLib.instance.api.crateApiListBookmarks();