refactor(providers): apply DisposableChangeNotifierMixin to 11 providers
This commit is contained in:
@@ -12,12 +12,13 @@ import '../services/companion_remote/lan_discovery_service.dart';
|
|||||||
import '../services/companion_remote/remote_auth_service.dart';
|
import '../services/companion_remote/remote_auth_service.dart';
|
||||||
import '../services/storage_service.dart';
|
import '../services/storage_service.dart';
|
||||||
import '../utils/app_logger.dart';
|
import '../utils/app_logger.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
|
|
||||||
export '../services/companion_remote/lan_discovery_service.dart' show DiscoveredHost;
|
export '../services/companion_remote/lan_discovery_service.dart' show DiscoveredHost;
|
||||||
|
|
||||||
typedef CommandReceivedCallback = void Function(RemoteCommand command);
|
typedef CommandReceivedCallback = void Function(RemoteCommand command);
|
||||||
|
|
||||||
class CompanionRemoteProvider with ChangeNotifier {
|
class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin {
|
||||||
RemoteSession? _session;
|
RemoteSession? _session;
|
||||||
CompanionRemotePeerService? _peerService;
|
CompanionRemotePeerService? _peerService;
|
||||||
LanDiscoveryService? _discoveryService;
|
LanDiscoveryService? _discoveryService;
|
||||||
@@ -97,7 +98,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_platform = Platform.operatingSystem;
|
_platform = Platform.operatingSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize crypto context from Plex home data.
|
/// Initialize crypto context from Plex home data.
|
||||||
@@ -159,7 +160,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
_session = RemoteSession(role: RemoteSessionRole.host, status: RemoteSessionStatus.connected);
|
_session = RemoteSession(role: RemoteSessionRole.host, status: RemoteSessionStatus.connected);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
// Start LAN discovery broadcasting
|
// Start LAN discovery broadcasting
|
||||||
_discoveryService ??= LanDiscoveryService();
|
_discoveryService ??= LanDiscoveryService();
|
||||||
@@ -181,7 +182,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
status: RemoteSessionStatus.error,
|
status: RemoteSessionStatus.error,
|
||||||
errorMessage: e.toString(),
|
errorMessage: e.toString(),
|
||||||
);
|
);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +200,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_session = null;
|
_session = null;
|
||||||
_isPlayerActive = false;
|
_isPlayerActive = false;
|
||||||
_intentionalDisconnect = false;
|
_intentionalDisconnect = false;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Client: Discovery ──
|
// ── Client: Discovery ──
|
||||||
@@ -237,7 +238,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_setupPeerServiceListeners();
|
_setupPeerServiceListeners();
|
||||||
|
|
||||||
_session = RemoteSession(role: RemoteSessionRole.remote, status: RemoteSessionStatus.connecting);
|
_session = RemoteSession(role: RemoteSessionRole.remote, status: RemoteSessionStatus.connecting);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final winner = await _peerService!.joinSessionRacing(
|
final winner = await _peerService!.joinSessionRacing(
|
||||||
@@ -252,12 +253,12 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_lastHostAddresses = [winner];
|
_lastHostAddresses = [winner];
|
||||||
|
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.connected);
|
_session = _session?.copyWith(status: RemoteSessionStatus.connected);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
appLogger.d('CompanionRemote: Connected to ${host.name} via $winner');
|
appLogger.d('CompanionRemote: Connected to ${host.name} via $winner');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appLogger.e('CompanionRemote: Failed to connect to host', error: e);
|
appLogger.e('CompanionRemote: Failed to connect to host', error: e);
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: e.toString());
|
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: e.toString());
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,7 +280,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_setupPeerServiceListeners();
|
_setupPeerServiceListeners();
|
||||||
|
|
||||||
_session = RemoteSession(role: RemoteSessionRole.remote, status: RemoteSessionStatus.connecting);
|
_session = RemoteSession(role: RemoteSessionRole.remote, status: RemoteSessionStatus.connecting);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _peerService!.joinSession(
|
await _peerService!.joinSession(
|
||||||
@@ -293,11 +294,11 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.connected);
|
_session = _session?.copyWith(status: RemoteSessionStatus.connected);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appLogger.e('CompanionRemote: Failed to connect to manual host', error: e);
|
appLogger.e('CompanionRemote: Failed to connect to manual host', error: e);
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: e.toString());
|
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: e.toString());
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,25 +328,25 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_deviceConnectedSubscription = _peerService!.onDeviceConnected.listen((device) {
|
_deviceConnectedSubscription = _peerService!.onDeviceConnected.listen((device) {
|
||||||
appLogger.d('CompanionRemote: Device connected: ${device.name}');
|
appLogger.d('CompanionRemote: Device connected: ${device.name}');
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.connected, connectedDevice: device);
|
_session = _session?.copyWith(status: RemoteSessionStatus.connected, connectedDevice: device);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
});
|
});
|
||||||
|
|
||||||
_deviceDisconnectedSubscription = _peerService!.onDeviceDisconnected.listen((_) {
|
_deviceDisconnectedSubscription = _peerService!.onDeviceDisconnected.listen((_) {
|
||||||
appLogger.d('CompanionRemote: Device disconnected (intentional: $_intentionalDisconnect)');
|
appLogger.d('CompanionRemote: Device disconnected (intentional: $_intentionalDisconnect)');
|
||||||
if (_intentionalDisconnect) {
|
if (_intentionalDisconnect) {
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, clearConnectedDevice: true);
|
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, clearConnectedDevice: true);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
} else if (isHost) {
|
} else if (isHost) {
|
||||||
_session = _session?.copyWith(
|
_session = _session?.copyWith(
|
||||||
status: RemoteSessionStatus.reconnecting,
|
status: RemoteSessionStatus.reconnecting,
|
||||||
clearConnectedDevice: true,
|
clearConnectedDevice: true,
|
||||||
clearErrorMessage: true,
|
clearErrorMessage: true,
|
||||||
);
|
);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
appLogger.d('CompanionRemote: Host waiting for client to reconnect');
|
appLogger.d('CompanionRemote: Host waiting for client to reconnect');
|
||||||
} else {
|
} else {
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.reconnecting);
|
_session = _session?.copyWith(status: RemoteSessionStatus.reconnecting);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
_scheduleReconnect();
|
_scheduleReconnect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -353,13 +354,13 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_errorSubscription = _peerService!.onError.listen((error) {
|
_errorSubscription = _peerService!.onError.listen((error) {
|
||||||
appLogger.e('CompanionRemote: Error: ${error.message}');
|
appLogger.e('CompanionRemote: Error: ${error.message}');
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: error.message);
|
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: error.message);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
});
|
});
|
||||||
|
|
||||||
_statusSubscription = _peerService!.onConnectionStateChanged.listen((status) {
|
_statusSubscription = _peerService!.onConnectionStateChanged.listen((status) {
|
||||||
appLogger.d('CompanionRemote: Status changed: $status');
|
appLogger.d('CompanionRemote: Status changed: $status');
|
||||||
_session = _session?.copyWith(status: status);
|
_session = _session?.copyWith(status: status);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +376,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
final device = RemoteDevice(id: id, name: name, platform: platform);
|
final device = RemoteDevice(id: id, name: name, platform: platform);
|
||||||
|
|
||||||
_session = _session?.copyWith(connectedDevice: device);
|
_session = _session?.copyWith(connectedDevice: device);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,7 +384,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
final playerActive = command.data?['playerActive'] as bool? ?? false;
|
final playerActive = command.data?['playerActive'] as bool? ?? false;
|
||||||
if (_isPlayerActive != playerActive) {
|
if (_isPlayerActive != playerActive) {
|
||||||
_isPlayerActive = playerActive;
|
_isPlayerActive = playerActive;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +423,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
errorMessage: 'Connection lost after $_maxReconnectAttempts attempts',
|
errorMessage: 'Connection lost after $_maxReconnectAttempts attempts',
|
||||||
);
|
);
|
||||||
_reconnectAttempts = 0;
|
_reconnectAttempts = 0;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,7 +439,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
if (_lastHostAddresses == null || !isCryptoReady) {
|
if (_lastHostAddresses == null || !isCryptoReady) {
|
||||||
appLogger.w('CompanionRemote: No stored context for reconnect');
|
appLogger.w('CompanionRemote: No stored context for reconnect');
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: 'Connection lost');
|
_session = _session?.copyWith(status: RemoteSessionStatus.error, errorMessage: 'Connection lost');
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,7 +465,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
|
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.connected, clearErrorMessage: true);
|
_session = _session?.copyWith(status: RemoteSessionStatus.connected, clearErrorMessage: true);
|
||||||
_reconnectAttempts = 0;
|
_reconnectAttempts = 0;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
appLogger.d('CompanionRemote: Reconnected successfully');
|
appLogger.d('CompanionRemote: Reconnected successfully');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appLogger.e('CompanionRemote: Reconnect failed', error: e);
|
appLogger.e('CompanionRemote: Reconnect failed', error: e);
|
||||||
@@ -484,7 +485,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
_reconnectTimer?.cancel();
|
_reconnectTimer?.cancel();
|
||||||
_reconnectAttempts = 0;
|
_reconnectAttempts = 0;
|
||||||
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, clearConnectedDevice: true);
|
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, clearConnectedDevice: true);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> leaveSession() async {
|
Future<void> leaveSession() async {
|
||||||
@@ -506,7 +507,7 @@ class CompanionRemoteProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
_isPlayerActive = false;
|
_isPlayerActive = false;
|
||||||
_intentionalDisconnect = false;
|
_intentionalDisconnect = false;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../services/storage_service.dart';
|
import '../services/storage_service.dart';
|
||||||
|
|
||||||
/// Provider for managing hidden library state across the app.
|
/// Provider for managing hidden library state across the app.
|
||||||
/// This ensures that when a library is hidden/unhidden in one screen,
|
/// This ensures that when a library is hidden/unhidden in one screen,
|
||||||
/// all other screens are automatically updated.
|
/// all other screens are automatically updated.
|
||||||
class HiddenLibrariesProvider extends ChangeNotifier {
|
class HiddenLibrariesProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
late StorageService _storageService;
|
late StorageService _storageService;
|
||||||
Set<String> _hiddenLibraryKeys = {};
|
Set<String> _hiddenLibraryKeys = {};
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
@@ -31,7 +32,7 @@ class HiddenLibrariesProvider extends ChangeNotifier {
|
|||||||
_storageService = await StorageService.getInstance();
|
_storageService = await StorageService.getInstance();
|
||||||
_hiddenLibraryKeys = _storageService.getHiddenLibraries();
|
_hiddenLibraryKeys = _storageService.getHiddenLibraries();
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hide a library by its key
|
/// Hide a library by its key
|
||||||
@@ -41,7 +42,7 @@ class HiddenLibrariesProvider extends ChangeNotifier {
|
|||||||
if (!_hiddenLibraryKeys.contains(libraryKey)) {
|
if (!_hiddenLibraryKeys.contains(libraryKey)) {
|
||||||
_hiddenLibraryKeys = Set.from(_hiddenLibraryKeys)..add(libraryKey);
|
_hiddenLibraryKeys = Set.from(_hiddenLibraryKeys)..add(libraryKey);
|
||||||
await _storageService.saveHiddenLibraries(_hiddenLibraryKeys);
|
await _storageService.saveHiddenLibraries(_hiddenLibraryKeys);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ class HiddenLibrariesProvider extends ChangeNotifier {
|
|||||||
if (_hiddenLibraryKeys.contains(libraryKey)) {
|
if (_hiddenLibraryKeys.contains(libraryKey)) {
|
||||||
_hiddenLibraryKeys = Set.from(_hiddenLibraryKeys)..remove(libraryKey);
|
_hiddenLibraryKeys = Set.from(_hiddenLibraryKeys)..remove(libraryKey);
|
||||||
await _storageService.saveHiddenLibraries(_hiddenLibraryKeys);
|
await _storageService.saveHiddenLibraries(_hiddenLibraryKeys);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,6 @@ class HiddenLibrariesProvider extends ChangeNotifier {
|
|||||||
/// Useful if storage was modified outside the provider
|
/// Useful if storage was modified outside the provider
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
_hiddenLibraryKeys = _storageService.getHiddenLibraries();
|
_hiddenLibraryKeys = _storageService.getHiddenLibraries();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../models/plex_library.dart';
|
import '../models/plex_library.dart';
|
||||||
import '../services/data_aggregation_service.dart';
|
import '../services/data_aggregation_service.dart';
|
||||||
import '../services/storage_service.dart';
|
import '../services/storage_service.dart';
|
||||||
@@ -12,7 +13,7 @@ enum LibrariesLoadState { initial, loading, loaded, error }
|
|||||||
/// Provider that serves as the single source of truth for library data.
|
/// Provider that serves as the single source of truth for library data.
|
||||||
/// Both SideNavigationRail and LibrariesScreen consume this provider
|
/// Both SideNavigationRail and LibrariesScreen consume this provider
|
||||||
/// instead of independently fetching library data.
|
/// instead of independently fetching library data.
|
||||||
class LibrariesProvider extends ChangeNotifier {
|
class LibrariesProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
DataAggregationService? _aggregationService;
|
DataAggregationService? _aggregationService;
|
||||||
List<PlexLibrary> _libraries = [];
|
List<PlexLibrary> _libraries = [];
|
||||||
LibrariesLoadState _loadState = LibrariesLoadState.initial;
|
LibrariesLoadState _loadState = LibrariesLoadState.initial;
|
||||||
@@ -60,7 +61,7 @@ class LibrariesProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
_loadState = LibrariesLoadState.loading;
|
_loadState = LibrariesLoadState.loading;
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch libraries from all servers
|
// Fetch libraries from all servers
|
||||||
@@ -79,12 +80,12 @@ class LibrariesProvider extends ChangeNotifier {
|
|||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
|
|
||||||
appLogger.i('LibrariesProvider: Loaded ${_libraries.length} libraries');
|
appLogger.i('LibrariesProvider: Loaded ${_libraries.length} libraries');
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
appLogger.e('LibrariesProvider: Failed to load libraries', error: e, stackTrace: stackTrace);
|
appLogger.e('LibrariesProvider: Failed to load libraries', error: e, stackTrace: stackTrace);
|
||||||
_loadState = LibrariesLoadState.error;
|
_loadState = LibrariesLoadState.error;
|
||||||
_errorMessage = e.toString();
|
_errorMessage = e.toString();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ class LibrariesProvider extends ChangeNotifier {
|
|||||||
/// Update the library order and persist it.
|
/// Update the library order and persist it.
|
||||||
Future<void> updateLibraryOrder(List<PlexLibrary> orderedLibraries) async {
|
Future<void> updateLibraryOrder(List<PlexLibrary> orderedLibraries) async {
|
||||||
_libraries = List.from(orderedLibraries);
|
_libraries = List.from(orderedLibraries);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
// Save the new order
|
// Save the new order
|
||||||
final storage = await StorageService.getInstance();
|
final storage = await StorageService.getInstance();
|
||||||
@@ -115,7 +116,7 @@ class LibrariesProvider extends ChangeNotifier {
|
|||||||
_libraries = [];
|
_libraries = [];
|
||||||
_loadState = LibrariesLoadState.initial;
|
_loadState = LibrariesLoadState.initial;
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
appLogger.d('LibrariesProvider: Cleared library data');
|
appLogger.d('LibrariesProvider: Cleared library data');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import '../i18n/strings.g.dart';
|
import '../i18n/strings.g.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../models/download_models.dart';
|
import '../models/download_models.dart';
|
||||||
import '../models/plex_metadata.dart';
|
import '../models/plex_metadata.dart';
|
||||||
import '../services/offline_watch_sync_service.dart';
|
import '../services/offline_watch_sync_service.dart';
|
||||||
@@ -18,7 +19,7 @@ import '../utils/global_key_utils.dart';
|
|||||||
/// - Effective watch status (local changes + cached server data)
|
/// - Effective watch status (local changes + cached server data)
|
||||||
/// - Offline "OnDeck" calculation for shows
|
/// - Offline "OnDeck" calculation for shows
|
||||||
/// - Manual mark watched/unwatched while offline
|
/// - Manual mark watched/unwatched while offline
|
||||||
class OfflineWatchProvider extends ChangeNotifier {
|
class OfflineWatchProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
final OfflineWatchSyncService _syncService;
|
final OfflineWatchSyncService _syncService;
|
||||||
final DownloadProvider _downloadProvider;
|
final DownloadProvider _downloadProvider;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ class OfflineWatchProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onSyncServiceChanged() {
|
void _onSyncServiceChanged() {
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a sync is in progress
|
/// Whether a sync is in progress
|
||||||
@@ -178,7 +179,7 @@ class OfflineWatchProvider extends ChangeNotifier {
|
|||||||
isNowWatched: true,
|
isNowWatched: true,
|
||||||
changeType: WatchStateChangeType.watched,
|
changeType: WatchStateChangeType.watched,
|
||||||
);
|
);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
_autoDeleteIfWatched(serverId, ratingKey);
|
_autoDeleteIfWatched(serverId, ratingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ class OfflineWatchProvider extends ChangeNotifier {
|
|||||||
isNowWatched: false,
|
isNowWatched: false,
|
||||||
changeType: WatchStateChangeType.unwatched,
|
changeType: WatchStateChangeType.unwatched,
|
||||||
);
|
);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get downloaded episodes for a show with their watch status.
|
/// Get downloaded episodes for a show with their watch status.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import '../models/plex_metadata.dart';
|
import '../models/plex_metadata.dart';
|
||||||
import '../models/play_queue_response.dart';
|
import '../models/play_queue_response.dart';
|
||||||
import '../services/plex_client.dart';
|
import '../services/plex_client.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
|
|
||||||
/// Result of trying to locate the current queue index.
|
/// Result of trying to locate the current queue index.
|
||||||
class _IndexLookupResult {
|
class _IndexLookupResult {
|
||||||
@@ -14,7 +15,7 @@ class _IndexLookupResult {
|
|||||||
|
|
||||||
/// Manages playback state using Plex's play queue API.
|
/// Manages playback state using Plex's play queue API.
|
||||||
/// This provider is session-only and does not persist across app restarts.
|
/// This provider is session-only and does not persist across app restarts.
|
||||||
class PlaybackStateProvider with ChangeNotifier {
|
class PlaybackStateProvider with ChangeNotifier, DisposableChangeNotifierMixin {
|
||||||
// Play queue state
|
// Play queue state
|
||||||
int? _playQueueId;
|
int? _playQueueId;
|
||||||
int _playQueueTotalCount = 0;
|
int _playQueueTotalCount = 0;
|
||||||
@@ -61,7 +62,7 @@ class PlaybackStateProvider with ChangeNotifier {
|
|||||||
void setCurrentItem(PlexMetadata metadata) {
|
void setCurrentItem(PlexMetadata metadata) {
|
||||||
if (_isQueueMode && metadata.playQueueItemID != null) {
|
if (_isQueueMode && metadata.playQueueItemID != null) {
|
||||||
_currentPlayQueueItemID = metadata.playQueueItemID;
|
_currentPlayQueueItemID = metadata.playQueueItemID;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ class PlaybackStateProvider with ChangeNotifier {
|
|||||||
|
|
||||||
_contextKey = contextKey;
|
_contextKey = contextKey;
|
||||||
_isQueueMode = true;
|
_isQueueMode = true;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load more items from the play queue if needed
|
/// Load more items from the play queue if needed
|
||||||
@@ -106,7 +107,7 @@ class PlaybackStateProvider with ChangeNotifier {
|
|||||||
// Use size or items length as fallback if totalCount is null
|
// Use size or items length as fallback if totalCount is null
|
||||||
_playQueueTotalCount = response.playQueueTotalCount ?? response.size ?? response.items!.length;
|
_playQueueTotalCount = response.playQueueTotalCount ?? response.size ?? response.items!.length;
|
||||||
_playQueueShuffled = response.playQueueShuffled;
|
_playQueueShuffled = response.playQueueShuffled;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -249,6 +250,6 @@ class PlaybackStateProvider with ChangeNotifier {
|
|||||||
_loadedItems = [];
|
_loadedItems = [];
|
||||||
_contextKey = null;
|
_contextKey = null;
|
||||||
_isQueueMode = false;
|
_isQueueMode = false;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../models/transcode_quality_preset.dart';
|
import '../models/transcode_quality_preset.dart';
|
||||||
import '../services/settings_service.dart';
|
import '../services/settings_service.dart';
|
||||||
|
|
||||||
class SettingsProvider extends ChangeNotifier {
|
class SettingsProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
SettingsService? _settingsService;
|
SettingsService? _settingsService;
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
Future<void>? _initFuture;
|
Future<void>? _initFuture;
|
||||||
@@ -21,7 +22,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
if (_isInitialized) return;
|
if (_isInitialized) return;
|
||||||
_settingsService = await SettingsService.getInstance();
|
_settingsService = await SettingsService.getInstance();
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-read settings after an external mutation (import, reset). The provider
|
/// Re-read settings after an external mutation (import, reset). The provider
|
||||||
@@ -30,7 +31,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
Future<void> reload() async {
|
Future<void> reload() async {
|
||||||
_settingsService = await SettingsService.getInstance();
|
_settingsService = await SettingsService.getInstance();
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
T _read<T>(Pref<T> pref, T fallback) => _isInitialized ? _settingsService!.read(pref) : fallback;
|
T _read<T>(Pref<T> pref, T fallback) => _isInitialized ? _settingsService!.read(pref) : fallback;
|
||||||
@@ -39,7 +40,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
if (!_isInitialized) await _initializeSettings();
|
if (!_isInitialized) await _initializeSettings();
|
||||||
if (_settingsService!.read(pref) == value) return;
|
if (_settingsService!.read(pref) == value) return;
|
||||||
await _settingsService!.write(pref, value);
|
await _settingsService!.write(pref, value);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
int get libraryDensity => _read(SettingsService.libraryDensity, LibraryDensity.defaultValue);
|
int get libraryDensity => _read(SettingsService.libraryDensity, LibraryDensity.defaultValue);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../models/shader_preset.dart';
|
import '../models/shader_preset.dart';
|
||||||
import '../services/settings_service.dart';
|
import '../services/settings_service.dart';
|
||||||
import '../services/shader_asset_loader.dart';
|
import '../services/shader_asset_loader.dart';
|
||||||
@@ -7,7 +8,7 @@ import '../services/shader_asset_loader.dart';
|
|||||||
/// Provider for managing shader preset state.
|
/// Provider for managing shader preset state.
|
||||||
///
|
///
|
||||||
/// Persists the selected shader preset so it is restored across sessions.
|
/// Persists the selected shader preset so it is restored across sessions.
|
||||||
class ShaderProvider extends ChangeNotifier {
|
class ShaderProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
late SettingsService _settingsService;
|
late SettingsService _settingsService;
|
||||||
|
|
||||||
ShaderPreset _savedPreset = ShaderPreset.none;
|
ShaderPreset _savedPreset = ShaderPreset.none;
|
||||||
@@ -31,7 +32,7 @@ class ShaderProvider extends ChangeNotifier {
|
|||||||
_currentPreset = _savedPreset;
|
_currentPreset = _savedPreset;
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the provider has finished initializing
|
/// Whether the provider has finished initializing
|
||||||
@@ -63,7 +64,7 @@ class ShaderProvider extends ChangeNotifier {
|
|||||||
_savedPreset = preset;
|
_savedPreset = preset;
|
||||||
_currentPreset = preset;
|
_currentPreset = preset;
|
||||||
await _settingsService.write(SettingsService.globalShaderPreset, preset.id);
|
await _settingsService.write(SettingsService.globalShaderPreset, preset.id);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the current preset without persisting (e.g. toggling off temporarily)
|
/// Update the current preset without persisting (e.g. toggling off temporarily)
|
||||||
@@ -84,7 +85,7 @@ class ShaderProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
_customPresets.add(preset);
|
_customPresets.add(preset);
|
||||||
await _saveCustomPresets();
|
await _saveCustomPresets();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
return preset;
|
return preset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ class ShaderProvider extends ChangeNotifier {
|
|||||||
await _settingsService.write(SettingsService.globalShaderPreset, ShaderPreset.none.id);
|
await _settingsService.write(SettingsService.globalShaderPreset, ShaderPreset.none.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveCustomPresets() async {
|
Future<void> _saveCustomPresets() async {
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import 'dart:io' show Platform;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../services/settings_service.dart' as settings;
|
import '../services/settings_service.dart' as settings;
|
||||||
import '../theme/mono_theme.dart';
|
import '../theme/mono_theme.dart';
|
||||||
|
|
||||||
class ThemeProvider extends ChangeNotifier {
|
class ThemeProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
late settings.SettingsService _settingsService;
|
late settings.SettingsService _settingsService;
|
||||||
settings.ThemeMode _themeMode = settings.ThemeMode.system;
|
settings.ThemeMode _themeMode = settings.ThemeMode.system;
|
||||||
late Brightness _systemBrightness;
|
late Brightness _systemBrightness;
|
||||||
@@ -19,7 +20,7 @@ class ThemeProvider extends ChangeNotifier {
|
|||||||
void _onBrightnessChanged() {
|
void _onBrightnessChanged() {
|
||||||
_systemBrightness = WidgetsBinding.instance.platformDispatcher.platformBrightness;
|
_systemBrightness = WidgetsBinding.instance.platformDispatcher.platformBrightness;
|
||||||
if (_themeMode == settings.ThemeMode.system) {
|
if (_themeMode == settings.ThemeMode.system) {
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ class ThemeProvider extends ChangeNotifier {
|
|||||||
_settingsService = await settings.SettingsService.getInstance();
|
_settingsService = await settings.SettingsService.getInstance();
|
||||||
_themeMode = _settingsService.read(settings.SettingsService.themeMode);
|
_themeMode = _settingsService.read(settings.SettingsService.themeMode);
|
||||||
_updateSplashTheme(_themeMode);
|
_updateSplashTheme(_themeMode);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ThemeMode get themeMode => _themeMode;
|
settings.ThemeMode get themeMode => _themeMode;
|
||||||
@@ -81,7 +82,7 @@ class ThemeProvider extends ChangeNotifier {
|
|||||||
_themeMode = mode;
|
_themeMode = mode;
|
||||||
await _settingsService.write(settings.SettingsService.themeMode, mode);
|
await _settingsService.write(settings.SettingsService.themeMode, mode);
|
||||||
_updateSplashTheme(mode);
|
_updateSplashTheme(mode);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ class ThemeProvider extends ChangeNotifier {
|
|||||||
final mode = _settingsService.read(settings.SettingsService.themeMode);
|
final mode = _settingsService.read(settings.SettingsService.themeMode);
|
||||||
_themeMode = mode;
|
_themeMode = mode;
|
||||||
_updateSplashTheme(mode);
|
_updateSplashTheme(mode);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateSplashTheme(settings.ThemeMode mode) {
|
void _updateSplashTheme(settings.ThemeMode mode) {
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ import '../services/trackers/tracker_connect_runner.dart';
|
|||||||
import '../services/trackers/tracker_constants.dart';
|
import '../services/trackers/tracker_constants.dart';
|
||||||
import '../services/trackers/tracker_coordinator.dart';
|
import '../services/trackers/tracker_coordinator.dart';
|
||||||
import '../utils/app_logger.dart';
|
import '../utils/app_logger.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
|
|
||||||
/// Owns the active MAL / AniList / Simkl sessions for the currently-selected
|
/// Owns the active MAL / AniList / Simkl sessions for the currently-selected
|
||||||
/// Plex profile. Single rebind seam: [onActiveProfileChanged] loads all three
|
/// Plex profile. Single rebind seam: [onActiveProfileChanged] loads all three
|
||||||
/// sessions from their stores and pushes them to their trackers.
|
/// sessions from their stores and pushes them to their trackers.
|
||||||
class TrackersProvider extends ChangeNotifier {
|
class TrackersProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
final MalAuthService _malAuth = MalAuthService();
|
final MalAuthService _malAuth = MalAuthService();
|
||||||
final AnilistAuthService _anilistAuth = AnilistAuthService();
|
final AnilistAuthService _anilistAuth = AnilistAuthService();
|
||||||
final SimklAuthService _simklAuth = SimklAuthService();
|
final SimklAuthService _simklAuth = SimklAuthService();
|
||||||
@@ -77,7 +78,7 @@ class TrackersProvider extends ChangeNotifier {
|
|||||||
_anilist = results[1] as AnilistSession?;
|
_anilist = results[1] as AnilistSession?;
|
||||||
_simkl = results[2] as SimklSession?;
|
_simkl = results[2] as SimklSession?;
|
||||||
_rebindAll();
|
_rebindAll();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ───── Connect / disconnect ─────
|
// ───── Connect / disconnect ─────
|
||||||
@@ -159,7 +160,7 @@ class TrackersProvider extends ChangeNotifier {
|
|||||||
if (_connecting != null || alreadyConnected) return false;
|
if (_connecting != null || alreadyConnected) return false;
|
||||||
_connecting = service;
|
_connecting = service;
|
||||||
_cancelCompleter = Completer<void>();
|
_cancelCompleter = Completer<void>();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
try {
|
try {
|
||||||
return await runConnectPipeline<T>(
|
return await runConnectPipeline<T>(
|
||||||
logLabel: service.name,
|
logLabel: service.name,
|
||||||
@@ -173,14 +174,14 @@ class TrackersProvider extends ChangeNotifier {
|
|||||||
if (c != null && !c.isCompleted) c.complete();
|
if (c != null && !c.isCompleted) c.complete();
|
||||||
_cancelCompleter = null;
|
_cancelCompleter = null;
|
||||||
_connecting = null;
|
_connecting = null;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _clearAndRebind<T>(TrackerAccountStore<T> store, void Function() clearAndRebind) async {
|
Future<void> _clearAndRebind<T>(TrackerAccountStore<T> store, void Function() clearAndRebind) async {
|
||||||
await store.clear(_activeUserUuid);
|
await store.clear(_activeUserUuid);
|
||||||
clearAndRebind();
|
clearAndRebind();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MalSession> _enrichMal(MalSession raw) async {
|
Future<MalSession> _enrichMal(MalSession raw) async {
|
||||||
@@ -240,7 +241,7 @@ class TrackersProvider extends ChangeNotifier {
|
|||||||
onSessionUpdated: (next) {
|
onSessionUpdated: (next) {
|
||||||
_mal = next;
|
_mal = next;
|
||||||
malAccountStore.save(_activeUserUuid, next);
|
malAccountStore.save(_activeUserUuid, next);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -263,7 +264,7 @@ class TrackersProvider extends ChangeNotifier {
|
|||||||
store.clear(_activeUserUuid);
|
store.clear(_activeUserUuid);
|
||||||
clearSession();
|
clearSession();
|
||||||
rebind();
|
rebind();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../models/trackers/device_code.dart';
|
import '../models/trackers/device_code.dart';
|
||||||
import '../services/trackers/tracker_connect_runner.dart';
|
import '../services/trackers/tracker_connect_runner.dart';
|
||||||
import '../services/trakt/trakt_account_store.dart';
|
import '../services/trakt/trakt_account_store.dart';
|
||||||
@@ -16,7 +17,7 @@ import '../utils/app_logger.dart';
|
|||||||
///
|
///
|
||||||
/// Single rebind seam: `onActiveProfileChanged` loads the new profile's
|
/// Single rebind seam: `onActiveProfileChanged` loads the new profile's
|
||||||
/// session and pushes it to both `TraktScrobbleService` and `TraktSyncService`.
|
/// session and pushes it to both `TraktScrobbleService` and `TraktSyncService`.
|
||||||
class TraktAccountProvider extends ChangeNotifier {
|
class TraktAccountProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
final TraktAuthService _auth = TraktAuthService();
|
final TraktAuthService _auth = TraktAuthService();
|
||||||
final _store = traktAccountStore;
|
final _store = traktAccountStore;
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ class TraktAccountProvider extends ChangeNotifier {
|
|||||||
if (c != null && !c.isCompleted) c.complete();
|
if (c != null && !c.isCompleted) c.complete();
|
||||||
_cancelCompleter = null;
|
_cancelCompleter = null;
|
||||||
_isConnecting = false;
|
_isConnecting = false;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ class TraktAccountProvider extends ChangeNotifier {
|
|||||||
session,
|
session,
|
||||||
onSessionInvalidated: _handleSessionInvalidated,
|
onSessionInvalidated: _handleSessionInvalidated,
|
||||||
);
|
);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by [TraktClient] when refresh fails permanently. Clears local state
|
/// Called by [TraktClient] when refresh fails permanently. Clears local state
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../mixins/disposable_change_notifier_mixin.dart';
|
||||||
import '../utils/plex_http_exception.dart';
|
import '../utils/plex_http_exception.dart';
|
||||||
import '../models/plex_home.dart';
|
import '../models/plex_home.dart';
|
||||||
import '../models/plex_home_user.dart';
|
import '../models/plex_home_user.dart';
|
||||||
@@ -8,7 +9,7 @@ import '../services/storage_service.dart';
|
|||||||
import '../utils/app_logger.dart';
|
import '../utils/app_logger.dart';
|
||||||
import '../screens/profile/pin_entry_dialog.dart';
|
import '../screens/profile/pin_entry_dialog.dart';
|
||||||
|
|
||||||
class UserProfileProvider extends ChangeNotifier {
|
class UserProfileProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
|
||||||
PlexHome? _home;
|
PlexHome? _home;
|
||||||
PlexHomeUser? _currentUser;
|
PlexHomeUser? _currentUser;
|
||||||
PlexUserProfile? _profileSettings;
|
PlexUserProfile? _profileSettings;
|
||||||
@@ -117,7 +118,7 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
// Profile settings are NOT cached - they will be fetched fresh from API
|
// Profile settings are NOT cached - they will be fetched fresh from API
|
||||||
// in refreshProfileSettings()
|
// in refreshProfileSettings()
|
||||||
|
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the user's profile settings from the API
|
/// Fetch the user's profile settings from the API
|
||||||
@@ -140,7 +141,7 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
appLogger.i('Successfully fetched user profile settings from API');
|
appLogger.i('Successfully fetched user profile settings from API');
|
||||||
|
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appLogger.w('Failed to fetch user profile settings from API', error: e);
|
appLogger.w('Failed to fetch user profile settings from API', error: e);
|
||||||
// Don't set error state, profile will remain null or keep existing value
|
// Don't set error state, profile will remain null or keep existing value
|
||||||
@@ -210,7 +211,7 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_setError('Failed to load home users: $e');
|
_setError('Failed to load home users: $e');
|
||||||
appLogger.e('Failed to load home users', error: e);
|
appLogger.e('Failed to load home users', error: e);
|
||||||
@@ -286,7 +287,7 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
// Invalidate all cached data and reconnect to all servers with new tokens
|
// Invalidate all cached data and reconnect to all servers with new tokens
|
||||||
// The callback will handle server reconnection using the servers list
|
// The callback will handle server reconnection using the servers list
|
||||||
@@ -337,7 +338,7 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
// Update current user from refreshed data
|
// Update current user from refreshed data
|
||||||
if (_home != null) {
|
if (_home != null) {
|
||||||
_currentUser = _home!.getUserByUUID(_currentUser!.uuid);
|
_currentUser = _home!.getUserByUUID(_currentUser!.uuid);
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +362,7 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
_isInitialized = false;
|
_isInitialized = false;
|
||||||
|
|
||||||
_clearError();
|
_clearError();
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
|
|
||||||
appLogger.i('User logged out successfully');
|
appLogger.i('User logged out successfully');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -373,12 +374,12 @@ class UserProfileProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
void _setLoading(bool loading) {
|
void _setLoading(bool loading) {
|
||||||
_isLoading = loading;
|
_isLoading = loading;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setError(String error) {
|
void _setError(String error) {
|
||||||
_error = error;
|
_error = error;
|
||||||
notifyListeners();
|
safeNotifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearError() {
|
void _clearError() {
|
||||||
|
|||||||
Reference in New Issue
Block a user