Files
Client/lib/storage/hydrated_storage_bootstrap.dart
T

81 lines
2.5 KiB
Dart

import 'dart:developer';
import 'dart:io';
import 'package:hydrated_bloc/hydrated_bloc.dart';
/// Opens the HydratedBloc box; a corrupt box is dropped and rebuilt, and if
/// even that fails the app runs on [InMemoryStorage] instead of never
/// reaching `runApp`.
Future<Storage> buildHydratedStorageWithFallback(String directoryPath) async {
final directory = HydratedStorageDirectory(directoryPath);
try {
return await _build(directory);
} catch (e, s) {
log('HydratedStorage open failed, rebuilding: $e', stackTrace: s);
}
try {
await _deleteHydratedBox(directoryPath);
return await _build(directory);
} catch (e, s) {
log('HydratedStorage rebuild failed, using memory: $e', stackTrace: s);
return InMemoryStorage();
}
}
final Expando<List<String> Function()> _keyReaders = Expando();
Future<Storage> _build(HydratedStorageDirectory directory) async {
final storage = await HydratedStorage.build(storageDirectory: directory);
// hydrated_bloc exposes no key listing; the Hive instance is the only way
// in. Every build replaces the static one, so it is captured per storage.
// ignore: invalid_use_of_visible_for_testing_member
final hive = HydratedStorage.hive;
_keyReaders[storage] = () =>
hive.box<dynamic>('hydrated_box').keys.map((k) => '$k').toList();
return storage;
}
/// All keys persisted in [storage], sorted; `null` when they can't be
/// enumerated.
List<String>? hydratedStorageKeys(Storage storage) {
if (storage is InMemoryStorage) return storage.keys.toList()..sort();
try {
return _keyReaders[storage]?.call()?..sort();
} on Object {
return null;
}
}
Future<void> _deleteHydratedBox(String directoryPath) async {
final directory = Directory(directoryPath);
if (!directory.existsSync()) return;
await for (final entity in directory.list()) {
final name = entity.uri.pathSegments.lastWhere((s) => s.isNotEmpty);
if (entity is File && name.startsWith('hydrated_box')) {
await entity.delete();
}
}
}
/// Non-persistent [Storage]; state lives for the session only.
class InMemoryStorage implements Storage {
final Map<String, dynamic> _values = {};
Iterable<String> get keys => _values.keys;
@override
dynamic read(String key) => _values[key];
@override
Future<void> write(String key, dynamic value) async => _values[key] = value;
@override
Future<void> delete(String key) async => _values.remove(key);
@override
Future<void> clear() async => _values.clear();
@override
Future<void> close() async {}
}