From 774ca8e4942cd4faf9bfa24586bfae5190acaa15 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 28 May 2025 17:11:54 +0200 Subject: [PATCH 1/6] feat: removed blocking wait for reconnect, mutagen initializing improvements --- App/App.xaml.cs | 27 ++++++++++--------- App/Services/RpcController.cs | 2 +- App/ViewModels/FileSyncListViewModel.cs | 12 ++++++++- .../TrayWindowDisconnectedViewModel.cs | 21 +++++++++++++-- .../Pages/TrayWindowDisconnectedPage.xaml | 11 ++++++++ .../Pages/TrayWindowDisconnectedPage.xaml.cs | 2 ++ App/Views/TrayWindow.xaml.cs | 3 +-- 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/App/App.xaml.cs b/App/App.xaml.cs index 5b82ced..fa9b005 100644 --- a/App/App.xaml.cs +++ b/App/App.xaml.cs @@ -165,20 +165,21 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) }, CancellationToken.None); // Initialize file sync. - var syncSessionCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - var syncSessionController = _services.GetRequiredService(); - _ = syncSessionController.RefreshState(syncSessionCts.Token).ContinueWith(t => - { - if (t.IsCanceled || t.Exception != null) - { - _logger.LogError(t.Exception, "failed to refresh sync state (canceled = {canceled})", t.IsCanceled); -#if DEBUG - Debugger.Break(); -#endif - } - syncSessionCts.Dispose(); - }, CancellationToken.None); + _ = Task.Delay(20000).ContinueWith((_) => + { + var syncSessionCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var syncSessionController = _services.GetRequiredService(); + syncSessionController.RefreshState(syncSessionCts.Token).ContinueWith( + t => + { + if (t.IsCanceled || t.Exception != null) + { + _logger.LogError(t.Exception, "failed to refresh sync state (canceled = {canceled})", t.IsCanceled); + } + syncSessionCts.Dispose(); + }, CancellationToken.None); + }); // Prevent the TrayWindow from closing, just hide it. var trayWindow = _services.GetRequiredService(); diff --git a/App/Services/RpcController.cs b/App/Services/RpcController.cs index 7beff66..7461ba8 100644 --- a/App/Services/RpcController.cs +++ b/App/Services/RpcController.cs @@ -313,7 +313,7 @@ private void SpeakerOnError(Exception e) Debug.WriteLine($"Error: {e}"); try { - Reconnect(CancellationToken.None).Wait(); + using var _ = Reconnect(CancellationToken.None); } catch { diff --git a/App/ViewModels/FileSyncListViewModel.cs b/App/ViewModels/FileSyncListViewModel.cs index 4777183..c682a4f 100644 --- a/App/ViewModels/FileSyncListViewModel.cs +++ b/App/ViewModels/FileSyncListViewModel.cs @@ -189,7 +189,7 @@ private void SyncSessionStateChanged(object? sender, SyncSessionControllerStateM UpdateSyncSessionState(syncSessionState); } - private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel credentialModel) + private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel credentialModel, SyncSessionControllerStateModel? syncSessionState = null) { var oldMessage = UnavailableMessage; if (rpcModel.RpcLifecycle != RpcLifecycle.Connected) @@ -204,6 +204,9 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede else if (rpcModel.VpnLifecycle != VpnLifecycle.Started) { UnavailableMessage = "Please start Coder Connect from the tray window to access file sync."; + } else if(syncSessionState != null && syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized) + { + UnavailableMessage = "Sync session controller is not initialized. Please wait..."; } else { @@ -219,6 +222,13 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede private void UpdateSyncSessionState(SyncSessionControllerStateModel syncSessionState) { + // This should never happen. + if (syncSessionState == null) + return; + if (syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized) + { + MaybeSetUnavailableMessage(_rpcController.GetState(), _credentialManager.GetCachedCredentials(), syncSessionState); + } Error = syncSessionState.DaemonError; Sessions = syncSessionState.SyncSessions.Select(s => new SyncSessionViewModel(this, s)).ToList(); } diff --git a/App/ViewModels/TrayWindowDisconnectedViewModel.cs b/App/ViewModels/TrayWindowDisconnectedViewModel.cs index 5fe16a2..ab7b3dd 100644 --- a/App/ViewModels/TrayWindowDisconnectedViewModel.cs +++ b/App/ViewModels/TrayWindowDisconnectedViewModel.cs @@ -1,8 +1,13 @@ -using System.Threading.Tasks; using Coder.Desktop.App.Models; using Coder.Desktop.App.Services; +using Coder.Desktop.App.Views.Pages; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System; +using System.Diagnostics; +using System.Threading.Tasks; namespace Coder.Desktop.App.ViewModels; @@ -11,6 +16,8 @@ public partial class TrayWindowDisconnectedViewModel : ObservableObject private readonly IRpcController _rpcController; [ObservableProperty] public partial bool ReconnectButtonEnabled { get; set; } = true; + [ObservableProperty] public partial string ErrorMessage { get; set; } = string.Empty; + [ObservableProperty] public partial bool ReconnectFailed { get; set; } = false; public TrayWindowDisconnectedViewModel(IRpcController rpcController) { @@ -26,6 +33,16 @@ private void UpdateFromRpcModel(RpcModel rpcModel) [RelayCommand] public async Task Reconnect() { - await _rpcController.Reconnect(); + try + { + ReconnectFailed = false; + ErrorMessage = string.Empty; + await _rpcController.Reconnect(); + } + catch (Exception ex) + { + ErrorMessage = ex.Message; + ReconnectFailed = true; + } } } diff --git a/App/Views/Pages/TrayWindowDisconnectedPage.xaml b/App/Views/Pages/TrayWindowDisconnectedPage.xaml index 6675f8d..7df7b7e 100644 --- a/App/Views/Pages/TrayWindowDisconnectedPage.xaml +++ b/App/Views/Pages/TrayWindowDisconnectedPage.xaml @@ -30,6 +30,17 @@ + + Reconnect failed + + + + Date: Wed, 28 May 2025 17:16:08 +0200 Subject: [PATCH 2/6] format fixes --- App/ViewModels/FileSyncListViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/App/ViewModels/FileSyncListViewModel.cs b/App/ViewModels/FileSyncListViewModel.cs index c682a4f..7daf475 100644 --- a/App/ViewModels/FileSyncListViewModel.cs +++ b/App/ViewModels/FileSyncListViewModel.cs @@ -204,7 +204,8 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede else if (rpcModel.VpnLifecycle != VpnLifecycle.Started) { UnavailableMessage = "Please start Coder Connect from the tray window to access file sync."; - } else if(syncSessionState != null && syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized) + } + else if (syncSessionState != null && syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized) { UnavailableMessage = "Sync session controller is not initialized. Please wait..."; } From 8aa6c7eb27d340f6e0e98952edcb03cfe007158b Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Thu, 29 May 2025 15:57:38 +0200 Subject: [PATCH 3/6] fixed delay from 20s to 5s --- App/App.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/App/App.xaml.cs b/App/App.xaml.cs index fa9b005..33ca6dc 100644 --- a/App/App.xaml.cs +++ b/App/App.xaml.cs @@ -166,7 +166,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) // Initialize file sync. - _ = Task.Delay(20000).ContinueWith((_) => + _ = Task.Delay(5000).ContinueWith((_) => { var syncSessionCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var syncSessionController = _services.GetRequiredService(); From bc338bc495072ffa2e8a9ee73d01d6e4476703d1 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:28:08 +0200 Subject: [PATCH 4/6] PR review --- App/App.xaml.cs | 1 + App/ViewModels/FileSyncListViewModel.cs | 12 +++++++----- App/ViewModels/TrayWindowDisconnectedViewModel.cs | 4 ---- App/Views/Pages/TrayWindowDisconnectedPage.xaml | 9 ++++++--- App/Views/Pages/TrayWindowDisconnectedPage.xaml.cs | 2 -- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/App/App.xaml.cs b/App/App.xaml.cs index 33ca6dc..06ab676 100644 --- a/App/App.xaml.cs +++ b/App/App.xaml.cs @@ -165,6 +165,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) }, CancellationToken.None); // Initialize file sync. + // We're adding a 5s delay here to avoid race conditions when loading the mutagen binary. _ = Task.Delay(5000).ContinueWith((_) => { diff --git a/App/ViewModels/FileSyncListViewModel.cs b/App/ViewModels/FileSyncListViewModel.cs index 7daf475..cb84f56 100644 --- a/App/ViewModels/FileSyncListViewModel.cs +++ b/App/ViewModels/FileSyncListViewModel.cs @@ -143,9 +143,9 @@ public void Initialize(Window window, DispatcherQueue dispatcherQueue) var rpcModel = _rpcController.GetState(); var credentialModel = _credentialManager.GetCachedCredentials(); - MaybeSetUnavailableMessage(rpcModel, credentialModel); var syncSessionState = _syncSessionController.GetState(); UpdateSyncSessionState(syncSessionState); + MaybeSetUnavailableMessage(rpcModel, credentialModel, syncSessionState); } private void RpcControllerStateChanged(object? sender, RpcModel rpcModel) @@ -159,7 +159,8 @@ private void RpcControllerStateChanged(object? sender, RpcModel rpcModel) } var credentialModel = _credentialManager.GetCachedCredentials(); - MaybeSetUnavailableMessage(rpcModel, credentialModel); + var syncSessionState = _syncSessionController.GetState(); + MaybeSetUnavailableMessage(rpcModel, credentialModel, syncSessionState); } private void CredentialManagerCredentialsChanged(object? sender, CredentialModel credentialModel) @@ -173,7 +174,8 @@ private void CredentialManagerCredentialsChanged(object? sender, CredentialModel } var rpcModel = _rpcController.GetState(); - MaybeSetUnavailableMessage(rpcModel, credentialModel); + var syncSessionState = _syncSessionController.GetState(); + MaybeSetUnavailableMessage(rpcModel, credentialModel, syncSessionState); } private void SyncSessionStateChanged(object? sender, SyncSessionControllerStateModel syncSessionState) @@ -189,7 +191,7 @@ private void SyncSessionStateChanged(object? sender, SyncSessionControllerStateM UpdateSyncSessionState(syncSessionState); } - private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel credentialModel, SyncSessionControllerStateModel? syncSessionState = null) + private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel credentialModel, SyncSessionControllerStateModel syncSessionState) { var oldMessage = UnavailableMessage; if (rpcModel.RpcLifecycle != RpcLifecycle.Connected) @@ -205,7 +207,7 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede { UnavailableMessage = "Please start Coder Connect from the tray window to access file sync."; } - else if (syncSessionState != null && syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized) + else if (syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized) { UnavailableMessage = "Sync session controller is not initialized. Please wait..."; } diff --git a/App/ViewModels/TrayWindowDisconnectedViewModel.cs b/App/ViewModels/TrayWindowDisconnectedViewModel.cs index ab7b3dd..ce6582c 100644 --- a/App/ViewModels/TrayWindowDisconnectedViewModel.cs +++ b/App/ViewModels/TrayWindowDisconnectedViewModel.cs @@ -1,12 +1,8 @@ using Coder.Desktop.App.Models; using Coder.Desktop.App.Services; -using Coder.Desktop.App.Views.Pages; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; using System; -using System.Diagnostics; using System.Threading.Tasks; namespace Coder.Desktop.App.ViewModels; diff --git a/App/Views/Pages/TrayWindowDisconnectedPage.xaml b/App/Views/Pages/TrayWindowDisconnectedPage.xaml index 7df7b7e..97bea3e 100644 --- a/App/Views/Pages/TrayWindowDisconnectedPage.xaml +++ b/App/Views/Pages/TrayWindowDisconnectedPage.xaml @@ -30,9 +30,12 @@ - - Reconnect failed - + + Date: Mon, 2 Jun 2025 13:28:53 +0200 Subject: [PATCH 5/6] fix --- App/Views/Pages/TrayWindowDisconnectedPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/App/Views/Pages/TrayWindowDisconnectedPage.xaml b/App/Views/Pages/TrayWindowDisconnectedPage.xaml index 97bea3e..b5b38e8 100644 --- a/App/Views/Pages/TrayWindowDisconnectedPage.xaml +++ b/App/Views/Pages/TrayWindowDisconnectedPage.xaml @@ -34,7 +34,7 @@ TextWrapping="Wrap" Foreground="Red" Visibility="{x:Bind ViewModel.ReconnectFailed, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" - Text ="Reconnect failed"/> + Text="Reconnect failed"/> Date: Mon, 2 Jun 2025 18:56:45 +0200 Subject: [PATCH 6/6] Update App/Views/Pages/TrayWindowDisconnectedPage.xaml Co-authored-by: Dean Sheather --- App/Views/Pages/TrayWindowDisconnectedPage.xaml | 1 - 1 file changed, 1 deletion(-) diff --git a/App/Views/Pages/TrayWindowDisconnectedPage.xaml b/App/Views/Pages/TrayWindowDisconnectedPage.xaml index b5b38e8..936c65f 100644 --- a/App/Views/Pages/TrayWindowDisconnectedPage.xaml +++ b/App/Views/Pages/TrayWindowDisconnectedPage.xaml @@ -36,7 +36,6 @@ Visibility="{x:Bind ViewModel.ReconnectFailed, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" Text="Reconnect failed"/> -