88 lines
3.2 KiB
Dart
88 lines
3.2 KiB
Dart
import 'dart:developer';
|
|
import 'dart:io';
|
|
|
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
|
import 'package:localstore/localstore.dart';
|
|
|
|
import '../api/request_cache.dart';
|
|
import '../session/account_codec.dart';
|
|
import 'hydrated_storage_bootstrap.dart';
|
|
|
|
/// Per-account local data: every account gets its own HydratedBloc box and
|
|
/// Localstore cache collection, so switching back is instant and offline.
|
|
/// Only the app settings live in the shared [global] box.
|
|
abstract final class AccountStorage {
|
|
static const _legacyCollection = 'MarianumMobile';
|
|
static const _settingsKey = 'SettingsCubit';
|
|
|
|
static late String _baseDir;
|
|
static final Map<String, Storage> _open = {};
|
|
static Storage _global = InMemoryStorage();
|
|
|
|
static Storage get global => _global;
|
|
|
|
/// Opens the global box. Settings of installs from before multi-account
|
|
/// support are copied over from the legacy box once.
|
|
static Future<void> init(String baseDir) async {
|
|
_baseDir = baseDir;
|
|
_global = await buildHydratedStorageWithFallback(
|
|
_ensureDir('$baseDir/hydrated_global'),
|
|
);
|
|
HydratedBloc.storage = InMemoryStorage();
|
|
if (_global.read(_settingsKey) != null) return;
|
|
final legacySettings = (await _storageFor('')).read(_settingsKey);
|
|
if (legacySettings != null) {
|
|
await _global.write(_settingsKey, legacySettings);
|
|
}
|
|
}
|
|
|
|
/// Points HydratedBloc and the request cache at [account]'s data. Blocs
|
|
/// keep the storage they were created with, so the per-account blocs have
|
|
/// to be recreated afterwards.
|
|
static Future<void> activate(AccountEntry? account) async {
|
|
if (account == null) {
|
|
HydratedBloc.storage = InMemoryStorage();
|
|
RequestCache.collection = _legacyCollection;
|
|
return;
|
|
}
|
|
HydratedBloc.storage = await _storageFor(account.namespace);
|
|
RequestCache.collection = cacheCollection(account.namespace);
|
|
}
|
|
|
|
/// Removes all local data of the account with [namespace].
|
|
static Future<void> delete(String namespace) async {
|
|
try {
|
|
final storage = await _storageFor(namespace);
|
|
await storage.clear();
|
|
// Closed boxes ignore writes, so blocs of the account that are still
|
|
// mounted until the remount cannot leave data behind.
|
|
await storage.close();
|
|
_open.remove(namespace);
|
|
if (namespace.isNotEmpty) {
|
|
final dir = Directory(_dirFor(namespace));
|
|
if (dir.existsSync()) await dir.delete(recursive: true);
|
|
}
|
|
await Localstore.instance.collection(cacheCollection(namespace)).delete();
|
|
} on Object catch (e, s) {
|
|
log('Account storage delete failed: $e', stackTrace: s);
|
|
}
|
|
}
|
|
|
|
static String cacheCollection(String namespace) =>
|
|
namespace.isEmpty ? _legacyCollection : '$_legacyCollection-$namespace';
|
|
|
|
static Future<Storage> _storageFor(String namespace) async =>
|
|
_open[namespace] ??= await buildHydratedStorageWithFallback(
|
|
_ensureDir(_dirFor(namespace)),
|
|
);
|
|
|
|
// The legacy account keeps the box at the root of the base directory.
|
|
static String _dirFor(String namespace) =>
|
|
namespace.isEmpty ? _baseDir : '$_baseDir/accounts/$namespace';
|
|
|
|
static String _ensureDir(String path) {
|
|
Directory(path).createSync(recursive: true);
|
|
return path;
|
|
}
|
|
}
|