import 'dart:async'; import 'dart:core'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:localstorage/localstorage.dart'; import 'package:path_provider/path_provider.dart'; import 'package:fluffychat/utils/platform_infos.dart'; // see https://github.com/mogol/flutter_secure_storage/issues/161#issuecomment-704578453 class AsyncMutex { Completer? _completer; Future lock() async { while (_completer != null) { await _completer!.future; } _completer = Completer(); } void unlock() { assert(_completer != null); final completer = _completer!; _completer = null; completer.complete(); } } class Store { LocalStorage? storage; final FlutterSecureStorage? secureStorage; static final _mutex = AsyncMutex(); Store() : secureStorage = PlatformInfos.isMobile ? const FlutterSecureStorage() : null; Future _setupLocalStorage() async { if (storage == null) { final directory = PlatformInfos.isBetaDesktop ? await getApplicationSupportDirectory() : (PlatformInfos.isWeb ? null : await getApplicationDocumentsDirectory()); storage = LocalStorage('LocalStorage', directory?.path); await storage!.ready; } } Future getItem(String key) async { if (!PlatformInfos.isMobile) { await _setupLocalStorage(); try { return storage!.getItem(key)?.toString(); } catch (_) { return null; } } try { await _mutex.lock(); return await secureStorage!.read(key: key); } catch (_) { return null; } finally { _mutex.unlock(); } } Future getItemBool(String key, [bool? defaultValue]) async { final value = await getItem(key); if (value == null) { return defaultValue; } // we also check for '1' for legacy reasons, some booleans were stored that way return value == '1' || value.toLowerCase() == 'true'; } Future setItem(String key, String? value) async { if (!PlatformInfos.isMobile) { await _setupLocalStorage(); return await storage!.setItem(key, value); } try { await _mutex.lock(); return await secureStorage!.write(key: key, value: value); } finally { _mutex.unlock(); } } Future setItemBool(String key, bool value) async { await setItem(key, value.toString()); } Future deleteItem(String key) async { if (!PlatformInfos.isMobile) { await _setupLocalStorage(); return await storage!.deleteItem(key); } try { await _mutex.lock(); return await secureStorage!.delete(key: key); } finally { _mutex.unlock(); } } }