@@ -117,6 +117,7 @@ class MusicAssistantProvider with ChangeNotifier {
117117 TrackMetadata ? _pendingTrackMetadata;
118118 TrackMetadata ? _currentNotificationMetadata;
119119 Completer <void >? _registrationInProgress;
120+ Completer <void >? _reconnectInProgress;
120121
121122 // Local player service
122123 late final LocalPlayerService _localPlayer;
@@ -1946,67 +1947,83 @@ class MusicAssistantProvider with ChangeNotifier {
19461947 }
19471948
19481949 Future <void > checkAndReconnect () async {
1950+ // Deduplicate: if a reconnect is already in progress, join it
1951+ if (_reconnectInProgress != null ) {
1952+ _logger.log ('🔄 checkAndReconnect called — already in progress, joining existing attempt' );
1953+ return _reconnectInProgress! .future;
1954+ }
1955+
19491956 _logger.log ('🔄 checkAndReconnect called - state: $_connectionState ' );
19501957
19511958 if (_serverUrl == null ) {
19521959 _logger.log ('🔄 No server URL saved, skipping reconnect' );
19531960 return ;
19541961 }
19551962
1956- // IMMEDIATELY load cached players for instant UI display
1957- // This makes mini player and device button appear instantly on app resume
1958- if (_availablePlayers.isEmpty && _cacheService.hasCachedPlayers) {
1959- _availablePlayers = _cacheService.getCachedPlayers ()! ;
1963+ _reconnectInProgress = Completer <void >();
19601964
1961- // Sort cached players immediately so list appears in correct order
1962- final smartSort = await SettingsService .getSmartSortPlayers ();
1963- final builtinPlayerId = await SettingsService .getBuiltinPlayerId ();
1964- _sortPlayersSync (_availablePlayers, smartSort, builtinPlayerId);
1965+ try {
1966+ // IMMEDIATELY load cached players for instant UI display
1967+ // This makes mini player and device button appear instantly on app resume
1968+ if (_availablePlayers.isEmpty && _cacheService.hasCachedPlayers) {
1969+ _availablePlayers = _cacheService.getCachedPlayers ()! ;
19651970
1966- _selectedPlayer = _cacheService.getCachedSelectedPlayer ();
1967- // Also try to restore from settings if cache doesn't have selected player
1968- if (_selectedPlayer == null && _availablePlayers.isNotEmpty) {
1969- final lastSelectedPlayerId = await SettingsService .getLastSelectedPlayerId ();
1970- if (lastSelectedPlayerId != null ) {
1971- try {
1972- _selectedPlayer = _availablePlayers.firstWhere (
1973- (p) => p.playerId == lastSelectedPlayerId,
1974- );
1975- } catch (e) {
1971+ // Sort cached players immediately so list appears in correct order
1972+ final smartSort = await SettingsService .getSmartSortPlayers ();
1973+ final builtinPlayerId = await SettingsService .getBuiltinPlayerId ();
1974+ _sortPlayersSync (_availablePlayers, smartSort, builtinPlayerId);
1975+
1976+ _selectedPlayer = _cacheService.getCachedSelectedPlayer ();
1977+ // Also try to restore from settings if cache doesn't have selected player
1978+ if (_selectedPlayer == null && _availablePlayers.isNotEmpty) {
1979+ final lastSelectedPlayerId = await SettingsService .getLastSelectedPlayerId ();
1980+ if (lastSelectedPlayerId != null ) {
1981+ try {
1982+ _selectedPlayer = _availablePlayers.firstWhere (
1983+ (p) => p.playerId == lastSelectedPlayerId,
1984+ );
1985+ } catch (e) {
1986+ _selectedPlayer = _availablePlayers.first;
1987+ }
1988+ } else {
19761989 _selectedPlayer = _availablePlayers.first;
19771990 }
1978- } else {
1979- _selectedPlayer = _availablePlayers.first;
19801991 }
1992+ _logger.log ('⚡ Loaded ${_availablePlayers .length } cached players instantly (sorted)' );
1993+ notifyListeners (); // Update UI immediately with cached data
19811994 }
1982- _logger.log ('⚡ Loaded ${_availablePlayers .length } cached players instantly (sorted)' );
1983- notifyListeners (); // Update UI immediately with cached data
1984- }
19851995
1986- if (_connectionState != MAConnectionState .connected &&
1987- _connectionState != MAConnectionState .authenticated) {
1988- _logger.log ('🔄 Not connected, attempting reconnect to $_serverUrl ' );
1989- try {
1990- await connectToServer (_serverUrl! );
1991- _logger.log ('🔄 Reconnection successful' );
1992- } catch (e) {
1993- _logger.log ('🔄 Reconnection failed: $e ' );
1994- }
1995- } else {
1996- _logger.log ('🔄 Already connected, verifying connection...' );
1997- try {
1998- await refreshPlayers ();
1999- await _updatePlayerState ();
2000- // Note: _preloadAdjacentPlayers is already called in refreshPlayers() -> _loadAndSelectPlayers()
2001- _logger.log ('🔄 Connection verified, players and state refreshed' );
2002- } catch (e) {
2003- _logger.log ('🔄 Connection verification failed, reconnecting: $e ' );
1996+ if (_connectionState != MAConnectionState .connected &&
1997+ _connectionState != MAConnectionState .authenticated) {
1998+ _logger.log ('🔄 Not connected, attempting reconnect to $_serverUrl ' );
20041999 try {
20052000 await connectToServer (_serverUrl! );
2006- } catch (reconnectError) {
2007- _logger.log ('🔄 Reconnection failed: $reconnectError ' );
2001+ _logger.log ('🔄 Reconnection successful' );
2002+ } catch (e) {
2003+ _logger.log ('🔄 Reconnection failed: $e ' );
2004+ }
2005+ } else {
2006+ _logger.log ('🔄 Already connected, verifying connection...' );
2007+ try {
2008+ await refreshPlayers ();
2009+ await _updatePlayerState ();
2010+ // Note: _preloadAdjacentPlayers is already called in refreshPlayers() -> _loadAndSelectPlayers()
2011+ _logger.log ('🔄 Connection verified, players and state refreshed' );
2012+ } catch (e) {
2013+ _logger.log ('🔄 Connection verification failed, reconnecting: $e ' );
2014+ try {
2015+ await connectToServer (_serverUrl! );
2016+ } catch (reconnectError) {
2017+ _logger.log ('🔄 Reconnection failed: $reconnectError ' );
2018+ }
20082019 }
20092020 }
2021+
2022+ _reconnectInProgress! .complete ();
2023+ } catch (e) {
2024+ _reconnectInProgress! .completeError (e);
2025+ } finally {
2026+ _reconnectInProgress = null ;
20102027 }
20112028 }
20122029
0 commit comments