diff --git a/apps/lib/data/cache/cache_scope.dart b/apps/lib/data/cache/cache_scope.dart new file mode 100644 index 0000000..6547fb8 --- /dev/null +++ b/apps/lib/data/cache/cache_scope.dart @@ -0,0 +1,28 @@ +typedef CacheScopeProvider = String? Function(); + +class CacheScope { + CacheScope._(); + + static CacheScopeProvider? _provider; + + static void configureProvider(CacheScopeProvider provider) { + _provider = provider; + } + + static void resetProvider() { + _provider = null; + } + + static String token() { + final raw = _provider?.call()?.trim(); + if (raw == null || raw.isEmpty) { + return 'anonymous'; + } + return raw; + } + + static String scopedKey(String key, {String? scopeToken}) { + final scope = scopeToken ?? token(); + return 'cache:$scope:$key'; + } +} diff --git a/apps/lib/data/cache/cache_store.dart b/apps/lib/data/cache/cache_store.dart index a6351e9..d92c823 100644 --- a/apps/lib/data/cache/cache_store.dart +++ b/apps/lib/data/cache/cache_store.dart @@ -3,6 +3,8 @@ import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; +import 'cache_scope.dart'; + class CacheEntry { const CacheEntry({required this.value, required this.fetchedAt}); @@ -196,7 +198,7 @@ class CacheInvalidator { void invalidate(String key) { final store = _store; if (store != null) { - unawaited(store.remove(key)); + unawaited(store.remove(CacheScope.scopedKey(key))); } } diff --git a/apps/lib/data/cache/cached_repository.dart b/apps/lib/data/cache/cached_repository.dart index ffeb8e4..79ef4e8 100644 --- a/apps/lib/data/cache/cached_repository.dart +++ b/apps/lib/data/cache/cached_repository.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'cache_scope.dart'; import 'cache_policy.dart'; import 'cache_store.dart'; @@ -31,18 +32,21 @@ abstract class CachedRepository { bool Function(T loaded)? shouldWriteLoaded, bool forceRefresh = false, }) async { + final scopeToken = CacheScope.token(); + final scopedKey = CacheScope.scopedKey(key, scopeToken: scopeToken); + if (forceRefresh) { return _refreshAndWrite( - key, + scopedKey, loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); } - final cached = await readCacheEntry(key); + final cached = await _readDecodedEntry(scopedKey); if (cached == null) { return _refreshAndWrite( - key, + scopedKey, loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); @@ -51,14 +55,14 @@ abstract class CachedRepository { final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt); if (decision.shouldRefreshInBackground) { refreshInBackground( - key: key, + key: scopedKey, loadFromRemote: loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); } if (decision.mustBlockForNetwork || !decision.canUseCached) { return _refreshAndWrite( - key, + scopedKey, loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); @@ -66,13 +70,13 @@ abstract class CachedRepository { return cached.value; } - Future?> readCacheEntry(String key) { - return _readDecodedEntry(key); + Future?> readCacheEntry(String key, {String? scopeToken}) { + return _readDecodedEntry(CacheScope.scopedKey(key, scopeToken: scopeToken)); } - Future writeCacheEntry(String key, T value) { + Future writeCacheEntry(String key, T value, {String? scopeToken}) { return store.write>( - key, + _scopedKey(key, scopeToken: scopeToken), CacheEntry(value: encodeValue(value), fetchedAt: now()), ); } @@ -93,8 +97,8 @@ abstract class CachedRepository { } } - Future removeCacheKey(String key) { - return store.remove(key); + Future removeCacheKey(String key, {String? scopeToken}) { + return store.remove(CacheScope.scopedKey(key, scopeToken: scopeToken)); } void refreshInBackground({ @@ -118,7 +122,7 @@ abstract class CachedRepository { } Future _refreshAndWrite( - String key, + String scopedKey, Future Function() loadFromRemote, { bool Function(T loaded)? shouldWriteLoaded, }) async { @@ -126,7 +130,14 @@ abstract class CachedRepository { if (shouldWriteLoaded != null && !shouldWriteLoaded(remote)) { return remote; } - await writeCacheEntry(key, remote); + await store.write>( + scopedKey, + CacheEntry(value: encodeValue(remote), fetchedAt: now()), + ); return remote; } + + String _scopedKey(String key, {String? scopeToken}) { + return CacheScope.scopedKey(key, scopeToken: scopeToken); + } } diff --git a/apps/lib/data/models/dial_codes.dart b/apps/lib/data/models/dial_codes.dart new file mode 100644 index 0000000..14efe79 --- /dev/null +++ b/apps/lib/data/models/dial_codes.dart @@ -0,0 +1,17 @@ +class DialCode { + const DialCode(this.value); + + final String value; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DialCode && + runtimeType == other.runtimeType && + value == other.value; + + @override + int get hashCode => value.hashCode; +} + +const kDialCodes = [DialCode('+86')];