diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index 260d571f..d52a1c6d 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -61,6 +61,35 @@ class StorageService extends BaseSharedPreferencesService { LogRedactionManager.registerToken(getPlexToken()); } + // User-scoped storage for per-profile library settings + + /// Returns `'user_{uuid}_'` for the current user, or `''` if no user is set. + String get _userPrefix { + final uuid = getCurrentUserUUID(); + return uuid != null ? 'user_${uuid}_' : ''; + } + + /// Read a string with user-scoped key, migrating from legacy key if needed. + String? _getScopedString(String baseKey) { + final scopedKey = '$_userPrefix$baseKey'; + final value = prefs.getString(scopedKey); + if (value != null || _userPrefix.isEmpty) return value; + // One-time migration from legacy global key + final legacy = prefs.getString(baseKey); + if (legacy != null) prefs.setString(scopedKey, legacy); + return legacy; + } + + /// Read an int with user-scoped key, migrating from legacy key if needed. + int? _getScopedInt(String baseKey) { + final scopedKey = '$_userPrefix$baseKey'; + final value = prefs.getInt(scopedKey); + if (value != null || _userPrefix.isEmpty) return value; + final legacy = prefs.getInt(baseKey); + if (legacy != null) prefs.setInt(scopedKey, legacy); + return legacy; + } + // Per-Server Endpoint URL (for multi-server connection caching) Future saveServerEndpoint(String serverId, String url) async { await prefs.setString('$_prefixServerEndpoint$serverId', url); @@ -101,34 +130,35 @@ class StorageService extends BaseSharedPreferencesService { } int? getSelectedLibraryIndex() { - return prefs.getInt(_keySelectedLibraryIndex); + return _getScopedInt(_keySelectedLibraryIndex); } // Selected Library Key (replaces index-based selection) Future saveSelectedLibraryKey(String key) async { - await prefs.setString(_keySelectedLibraryKey, key); + await prefs.setString('$_userPrefix$_keySelectedLibraryKey', key); } String? getSelectedLibraryKey() { - return prefs.getString(_keySelectedLibraryKey); + return _getScopedString(_keySelectedLibraryKey); } // Library Filters (stored as JSON string) Future saveLibraryFilters(Map filters, {String? sectionId}) async { - final key = sectionId != null ? '$_prefixLibraryFilters$sectionId' : _keyLibraryFilters; + final baseKey = sectionId != null ? '$_prefixLibraryFilters$sectionId' : _keyLibraryFilters; // Note: using Map which json.encode handles correctly final jsonString = json.encode(filters); - await prefs.setString(key, jsonString); + await prefs.setString('$_userPrefix$baseKey', jsonString); } Map getLibraryFilters({String? sectionId}) { - final scopedKey = sectionId != null ? '$_prefixLibraryFilters$sectionId' : _keyLibraryFilters; + final baseKey = sectionId != null ? '$_prefixLibraryFilters$sectionId' : _keyLibraryFilters; // Prefer per-library filters when available - final jsonString = - prefs.getString(scopedKey) ?? - // Legacy support: fall back to global filters if present - prefs.getString(_keyLibraryFilters); + var jsonString = _getScopedString(baseKey); + if (jsonString == null && sectionId != null) { + // Legacy support: fall back to global filters if present + jsonString = _getScopedString(_keyLibraryFilters); + } if (jsonString == null) return {}; final decoded = decodeJsonStringToMap(jsonString); @@ -138,38 +168,45 @@ class StorageService extends BaseSharedPreferencesService { // Library Sort (per-library, stored individually with descending flag) Future saveLibrarySort(String sectionId, String sortKey, {bool descending = false}) async { final sortData = {'key': sortKey, 'descending': descending}; - await _setJsonMap('$_prefixLibrarySort$sectionId', sortData); + await _setJsonMap('$_userPrefix$_prefixLibrarySort$sectionId', sortData); } Map? getLibrarySort(String sectionId) { - return _readJsonMap('$_prefixLibrarySort$sectionId', legacyStringOk: true); + final baseKey = '$_prefixLibrarySort$sectionId'; + final scopedKey = '$_userPrefix$baseKey'; + var result = _readJsonMap(scopedKey, legacyStringOk: true); + if (result != null || _userPrefix.isEmpty) return result; + // One-time migration from legacy key + result = _readJsonMap(baseKey, legacyStringOk: true); + if (result != null) _setJsonMap(scopedKey, result); + return result; } // Library Grouping (per-library, e.g., 'movies', 'shows', 'seasons', 'episodes') Future saveLibraryGrouping(String sectionId, String grouping) async { - await prefs.setString('$_prefixLibraryGrouping$sectionId', grouping); + await prefs.setString('$_userPrefix$_prefixLibraryGrouping$sectionId', grouping); } String? getLibraryGrouping(String sectionId) { - return prefs.getString('$_prefixLibraryGrouping$sectionId'); + return _getScopedString('$_prefixLibraryGrouping$sectionId'); } // Library Tab (per-library, saves last selected tab index) Future saveLibraryTab(String sectionId, int tabIndex) async { - await prefs.setInt('$_prefixLibraryTab$sectionId', tabIndex); + await prefs.setInt('$_userPrefix$_prefixLibraryTab$sectionId', tabIndex); } int? getLibraryTab(String sectionId) { - return prefs.getInt('$_prefixLibraryTab$sectionId'); + return _getScopedInt('$_prefixLibraryTab$sectionId'); } // Hidden Libraries (stored as JSON array of library section IDs) Future saveHiddenLibraries(Set libraryKeys) async { - await _setStringList(_keyHiddenLibraries, libraryKeys.toList()); + await _setStringList('$_userPrefix$_keyHiddenLibraries', libraryKeys.toList()); } Set getHiddenLibraries() { - final jsonString = prefs.getString(_keyHiddenLibraries); + final jsonString = _getScopedString(_keyHiddenLibraries); if (jsonString == null) return {}; try { @@ -180,23 +217,34 @@ class StorageService extends BaseSharedPreferencesService { } } - // Clear library preferences + // Clear library preferences (scoped to current user) Future clearLibraryPreferences() async { + final prefix = _userPrefix; await Future.wait([ - ..._libraryPreferenceKeys.map((k) => prefs.remove(k)), - _clearKeysWithPrefix(_prefixLibrarySort), - _clearKeysWithPrefix(_prefixLibraryFilters), - _clearKeysWithPrefix(_prefixLibraryGrouping), - _clearKeysWithPrefix(_prefixLibraryTab), + ..._libraryPreferenceKeys.map((k) => prefs.remove('$prefix$k')), + prefs.remove('$prefix$_keySelectedLibraryKey'), + _clearKeysWithPrefix('$prefix$_prefixLibrarySort'), + _clearKeysWithPrefix('$prefix$_prefixLibraryFilters'), + _clearKeysWithPrefix('$prefix$_prefixLibraryGrouping'), + _clearKeysWithPrefix('$prefix$_prefixLibraryTab'), ]); } // Library Order (stored as JSON list of library keys) Future saveLibraryOrder(List libraryKeys) async { - await _setStringList(_keyLibraryOrder, libraryKeys); + await _setStringList('$_userPrefix$_keyLibraryOrder', libraryKeys); } - List? getLibraryOrder() => _getStringList(_keyLibraryOrder); + List? getLibraryOrder() { + final baseKey = _keyLibraryOrder; + final scopedKey = '$_userPrefix$baseKey'; + final value = _getStringList(scopedKey); + if (value != null || _userPrefix.isEmpty) return value; + // One-time migration from legacy key + final legacy = _getStringList(baseKey); + if (legacy != null) _setStringList(scopedKey, legacy); + return legacy; + } // User Profile (stored as JSON string) Future saveUserProfile(Map profileJson) async {