Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit51bf68e

Browse files
authored
feat: show vpn start/stop failure in app (#44)
Adds red text that appears when the VPN fails to start or stop. After anerror, any manual start/stop operation will clear the error.Contributes to#40
1 parente1d9774 commit51bf68e

File tree

6 files changed

+201
-169
lines changed

6 files changed

+201
-169
lines changed

‎App/Converters/AgentStatusToColorConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Coder.Desktop.App.Converters;
99
publicclassAgentStatusToColorConverter:IValueConverter
1010
{
1111
privatestaticreadonlySolidColorBrushGreen=new(Color.FromArgb(255,52,199,89));
12-
privatestaticreadonlySolidColorBrushYellow=new(Color.FromArgb(255,204,1,0));
12+
privatestaticreadonlySolidColorBrushYellow=new(Color.FromArgb(255,255,204,1));
1313
privatestaticreadonlySolidColorBrushRed=new(Color.FromArgb(255,255,59,48));
1414
privatestaticreadonlySolidColorBrushGray=new(Color.FromArgb(255,142,142,147));
1515

‎App/Converters/InverseBoolToVisibilityConverter.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

‎App/Converters/VpnLifecycleToVisibilityConverter.cs

Lines changed: 0 additions & 14 deletions
This file was deleted.

‎App/Services/RpcController.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ public async Task Reconnect(CancellationToken ct = default)
146146
Status=newStatusRequest(),
147147
},ct);
148148
if(statusReply.MsgCase!=ServiceMessage.MsgOneofCase.Status)
149-
thrownewInvalidOperationException($"Unexpected reply message type:{statusReply.MsgCase}");
149+
thrownewVpnLifecycleException(
150+
$"Failed to get VPN status. Unexpected reply message type:{statusReply.MsgCase}");
150151
ApplyStatusUpdate(statusReply.Status);
151152
}
152153

@@ -172,20 +173,26 @@ public async Task StartVpn(CancellationToken ct = default)
172173
ApiToken=credentials.ApiToken,
173174
},
174175
},ct);
175-
if(reply.MsgCase!=ServiceMessage.MsgOneofCase.Start)
176-
thrownewInvalidOperationException($"Unexpected reply message type:{reply.MsgCase}");
177176
}
178177
catch(Exceptione)
179178
{
180179
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Stopped;});
181180
thrownewRpcOperationException("Failed to send start command to service",e);
182181
}
183182

183+
if(reply.MsgCase!=ServiceMessage.MsgOneofCase.Start)
184+
{
185+
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Unknown;});
186+
thrownewVpnLifecycleException($"Failed to start VPN. Unexpected reply message type:{reply.MsgCase}");
187+
}
188+
184189
if(!reply.Start.Success)
185190
{
191+
// We use Stopped instead of Unknown here as it's usually the case
192+
// that a failed start got cleaned up successfully.
186193
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Stopped;});
187-
thrownewVpnLifecycleException("Failed to start VPN",
188-
newInvalidOperationException($"Service reported failure:{reply.Start.ErrorMessage}"));
194+
thrownewVpnLifecycleException(
195+
$"Failed to start VPN.Service reported failure:{reply.Start.ErrorMessage}");
189196
}
190197

191198
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Started;});
@@ -212,16 +219,20 @@ public async Task StopVpn(CancellationToken ct = default)
212219
}
213220
finally
214221
{
215-
// Technically the state is unknown now.
216-
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Stopped;});
222+
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Unknown;});
217223
}
218224

219225
if(reply.MsgCase!=ServiceMessage.MsgOneofCase.Stop)
220-
thrownewVpnLifecycleException("Failed to stop VPN",
221-
newInvalidOperationException($"Unexpected reply message type:{reply.MsgCase}"));
226+
{
227+
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Unknown;});
228+
thrownewVpnLifecycleException($"Failed to stop VPN. Unexpected reply message type:{reply.MsgCase}");
229+
}
230+
222231
if(!reply.Stop.Success)
223-
thrownewVpnLifecycleException("Failed to stop VPN",
224-
newInvalidOperationException($"Service reported failure:{reply.Stop.ErrorMessage}"));
232+
{
233+
MutateState(state=>{state.VpnLifecycle=VpnLifecycle.Unknown;});
234+
thrownewVpnLifecycleException($"Failed to stop VPN. Service reported failure:{reply.Stop.ErrorMessage}");
235+
}
225236
}
226237

227238
publicasyncValueTaskDisposeAsync()

‎App/ViewModels/TrayWindowViewModel.cs

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
usingSystem;
22
usingSystem.Collections.Generic;
33
usingSystem.Linq;
4+
usingSystem.Threading.Tasks;
45
usingCoder.Desktop.App.Models;
56
usingCoder.Desktop.App.Services;
67
usingCoder.Desktop.Vpn.Proto;
@@ -10,6 +11,7 @@
1011
usingMicrosoft.UI.Dispatching;
1112
usingMicrosoft.UI.Xaml;
1213
usingMicrosoft.UI.Xaml.Controls;
14+
usingException=System.Exception;
1315

1416
namespaceCoder.Desktop.App.ViewModels;
1517

@@ -23,22 +25,45 @@ public partial class TrayWindowViewModel : ObservableObject
2325

2426
privateDispatcherQueue?_dispatcherQueue;
2527

26-
[ObservableProperty]publicpartialVpnLifecycleVpnLifecycle{get;set;}=VpnLifecycle.Unknown;
28+
[ObservableProperty]
29+
[NotifyPropertyChangedFor(nameof(ShowEnableSection))]
30+
[NotifyPropertyChangedFor(nameof(ShowWorkspacesHeader))]
31+
[NotifyPropertyChangedFor(nameof(ShowNoAgentsSection))]
32+
[NotifyPropertyChangedFor(nameof(ShowAgentsSection))]
33+
publicpartialVpnLifecycleVpnLifecycle{get;set;}=VpnLifecycle.Unknown;
2734

2835
// This is a separate property because we need the switch to be 2-way.
2936
[ObservableProperty]publicpartialboolVpnSwitchActive{get;set;}=false;
3037

31-
[ObservableProperty]publicpartialstring?VpnFailedMessage{get;set;}=null;
38+
[ObservableProperty]
39+
[NotifyPropertyChangedFor(nameof(ShowEnableSection))]
40+
[NotifyPropertyChangedFor(nameof(ShowWorkspacesHeader))]
41+
[NotifyPropertyChangedFor(nameof(ShowNoAgentsSection))]
42+
[NotifyPropertyChangedFor(nameof(ShowAgentsSection))]
43+
[NotifyPropertyChangedFor(nameof(ShowAgentOverflowButton))]
44+
[NotifyPropertyChangedFor(nameof(ShowFailedSection))]
45+
publicpartialstring?VpnFailedMessage{get;set;}=null;
3246

3347
[ObservableProperty]
34-
[NotifyPropertyChangedFor(nameof(NoAgents))]
35-
[NotifyPropertyChangedFor(nameof(AgentOverflow))]
3648
[NotifyPropertyChangedFor(nameof(VisibleAgents))]
49+
[NotifyPropertyChangedFor(nameof(ShowNoAgentsSection))]
50+
[NotifyPropertyChangedFor(nameof(ShowAgentsSection))]
51+
[NotifyPropertyChangedFor(nameof(ShowAgentOverflowButton))]
3752
publicpartialList<AgentViewModel>Agents{get;set;}=[];
3853

39-
publicboolNoAgents=>Agents.Count==0;
54+
publicboolShowEnableSection=>VpnFailedMessageisnull&&VpnLifecycleis notVpnLifecycle.Started;
55+
56+
publicboolShowWorkspacesHeader=>VpnFailedMessageisnull&&VpnLifecycleisVpnLifecycle.Started;
57+
58+
publicboolShowNoAgentsSection=>
59+
VpnFailedMessageisnull&&Agents.Count==0&&VpnLifecycleisVpnLifecycle.Started;
60+
61+
publicboolShowAgentsSection=>
62+
VpnFailedMessageisnull&&Agents.Count>0&&VpnLifecycleisVpnLifecycle.Started;
63+
64+
publicboolShowFailedSection=>VpnFailedMessageis notnull;
4065

41-
publicboolAgentOverflow=>Agents.Count>MaxAgents;
66+
publicboolShowAgentOverflowButton=>VpnFailedMessageisnull&&Agents.Count>MaxAgents;
4267

4368
[ObservableProperty]
4469
[NotifyPropertyChangedFor(nameof(VisibleAgents))]
@@ -190,24 +215,47 @@ public void VpnSwitch_Toggled(object sender, RoutedEventArgs e)
190215
{
191216
if(senderis notToggleSwitchtoggleSwitch)return;
192217

193-
VpnFailedMessage="";
218+
VpnFailedMessage=null;
219+
220+
// The start/stop methods will call back to update the state.
221+
if(toggleSwitch.IsOn&&VpnLifecycleisVpnLifecycle.Stopped)
222+
_=StartVpn();// in the background
223+
elseif(!toggleSwitch.IsOn&&VpnLifecycleisVpnLifecycle.Started)
224+
_=StopVpn();// in the background
225+
else
226+
toggleSwitch.IsOn=VpnLifecycleisVpnLifecycle.Starting orVpnLifecycle.Started;
227+
}
228+
229+
privateasyncTaskStartVpn()
230+
{
194231
try
195232
{
196-
// The start/stop methods will call back to update the state.
197-
if(toggleSwitch.IsOn&&VpnLifecycleisVpnLifecycle.Stopped)
198-
_rpcController.StartVpn();
199-
elseif(!toggleSwitch.IsOn&&VpnLifecycleisVpnLifecycle.Started)
200-
_rpcController.StopVpn();
201-
else
202-
toggleSwitch.IsOn=VpnLifecycleisVpnLifecycle.Starting orVpnLifecycle.Started;
233+
await_rpcController.StartVpn();
203234
}
204-
catch
235+
catch(Exceptione)
205236
{
206-
// TODO: display error
207-
VpnFailedMessage=e.ToString();
237+
VpnFailedMessage="Failed to start CoderVPN: "+MaybeUnwrapTunnelError(e);
208238
}
209239
}
210240

241+
privateasyncTaskStopVpn()
242+
{
243+
try
244+
{
245+
await_rpcController.StopVpn();
246+
}
247+
catch(Exceptione)
248+
{
249+
VpnFailedMessage="Failed to stop CoderVPN: "+MaybeUnwrapTunnelError(e);
250+
}
251+
}
252+
253+
privatestaticstringMaybeUnwrapTunnelError(Exceptione)
254+
{
255+
if(eisVpnLifecycleExceptionvpnError)returnvpnError.Message;
256+
returne.ToString();
257+
}
258+
211259
[RelayCommand]
212260
publicvoidToggleShowAllAgents()
213261
{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp