@@ -21,7 +21,7 @@ public partial class FileSyncListViewModel : ObservableObject
21
21
{
22
22
private Window ? _window ;
23
23
private DispatcherQueue ? _dispatcherQueue ;
24
- private Window ? _remotePickerWindow ;
24
+ private DirectoryPickerWindow ? _remotePickerWindow ;
25
25
26
26
private readonly ISyncSessionController _syncSessionController ;
27
27
private readonly IRpcController _rpcController ;
@@ -50,7 +50,7 @@ public partial class FileSyncListViewModel : ObservableObject
50
50
51
51
[ ObservableProperty ] public partial bool OperationInProgress { get ; set ; } = false ;
52
52
53
- [ ObservableProperty ] public partial List < SyncSessionViewModel > Sessions { get ; set ; } = [ ] ;
53
+ [ ObservableProperty ] public partial IReadOnlyList < SyncSessionViewModel > Sessions { get ; set ; } = [ ] ;
54
54
55
55
[ ObservableProperty ] public partial bool CreatingNewSession { get ; set ; } = false ;
56
56
@@ -62,18 +62,29 @@ public partial class FileSyncListViewModel : ObservableObject
62
62
[ NotifyPropertyChangedFor ( nameof ( NewSessionCreateEnabled ) ) ]
63
63
public partial bool NewSessionLocalPathDialogOpen { get ; set ; } = false ;
64
64
65
+ [ ObservableProperty ]
66
+ [ NotifyPropertyChangedFor ( nameof ( NewSessionRemoteHostEnabled ) ) ]
67
+ public partial IReadOnlyList < string > AvailableHosts { get ; set ; } = [ ] ;
68
+
65
69
[ ObservableProperty ]
66
70
[ NotifyPropertyChangedFor ( nameof ( NewSessionCreateEnabled ) ) ]
67
- public partial string NewSessionRemoteHost { get ; set ; } = "" ;
71
+ [ NotifyPropertyChangedFor ( nameof ( NewSessionRemotePathDialogEnabled ) ) ]
72
+ public partial string ? NewSessionRemoteHost { get ; set ; } = null ;
68
73
69
74
[ ObservableProperty ]
70
75
[ NotifyPropertyChangedFor ( nameof ( NewSessionCreateEnabled ) ) ]
71
76
public partial string NewSessionRemotePath { get ; set ; } = "" ;
72
77
73
78
[ ObservableProperty ]
74
79
[ NotifyPropertyChangedFor ( nameof ( NewSessionCreateEnabled ) ) ]
80
+ [ NotifyPropertyChangedFor ( nameof ( NewSessionRemotePathDialogEnabled ) ) ]
75
81
public partial bool NewSessionRemotePathDialogOpen { get ; set ; } = false ;
76
82
83
+ public bool NewSessionRemoteHostEnabled => AvailableHosts . Count > 0 ;
84
+
85
+ public bool NewSessionRemotePathDialogEnabled =>
86
+ ! string . IsNullOrWhiteSpace ( NewSessionRemoteHost ) && ! NewSessionRemotePathDialogOpen ;
87
+
77
88
public bool NewSessionCreateEnabled
78
89
{
79
90
get
@@ -109,17 +120,6 @@ public void Initialize(Window window, DispatcherQueue dispatcherQueue)
109
120
if ( ! _dispatcherQueue . HasThreadAccess )
110
121
throw new InvalidOperationException ( "Initialize must be called from the UI thread" ) ;
111
122
112
- // Force the remote picker to activate when activating the file sync
113
- // list window if open.
114
- _window . Activated += ( _ , e ) =>
115
- {
116
- if ( _remotePickerWindow is not null && e . WindowActivationState is WindowActivationState . PointerActivated )
117
- {
118
- e . Handled = true ;
119
- _remotePickerWindow . Activate ( ) ;
120
- }
121
- } ;
122
-
123
123
_rpcController . StateChanged += RpcControllerStateChanged ;
124
124
_credentialManager . CredentialsChanged += CredentialManagerCredentialsChanged ;
125
125
_syncSessionController . StateChanged += SyncSessionStateChanged ;
@@ -199,8 +199,13 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede
199
199
else
200
200
{
201
201
UnavailableMessage = null ;
202
+ // Reload if we transitioned from unavailable to available.
202
203
if ( oldMessage != null ) ReloadSessions ( ) ;
203
204
}
205
+
206
+ // When transitioning from available to unavailable:
207
+ if ( oldMessage == null && UnavailableMessage != null )
208
+ ClearNewForm ( ) ;
204
209
}
205
210
206
211
private void UpdateSyncSessionState ( SyncSessionControllerStateModel syncSessionState )
@@ -253,10 +258,34 @@ private void HandleRefresh(Task<SyncSessionControllerStateModel> t)
253
258
Loading = false ;
254
259
}
255
260
261
+ // Overriding AvailableHosts seems to make the ComboBox clear its value, so
262
+ // we only do this while the create form is not open.
263
+ // Must be called in UI thread.
264
+ private void SetAvailableHostsFromRpcModel ( RpcModel rpcModel )
265
+ {
266
+ var hosts = new List < string > ( rpcModel . Agents . Count ) ;
267
+ // Agents will only contain started agents.
268
+ foreach ( var agent in rpcModel . Agents )
269
+ {
270
+ var fqdn = agent . Fqdn
271
+ . Select ( a => a . Trim ( '.' ) )
272
+ . Where ( a => ! string . IsNullOrWhiteSpace ( a ) )
273
+ . Aggregate ( ( a , b ) => a . Count ( c => c == '.' ) < b . Count ( c => c == '.' ) ? a : b ) ;
274
+ if ( string . IsNullOrWhiteSpace ( fqdn ) )
275
+ continue ;
276
+ hosts . Add ( fqdn ) ;
277
+ }
278
+
279
+ NewSessionRemoteHost = null ;
280
+ AvailableHosts = hosts ;
281
+ }
282
+
256
283
[ RelayCommand ]
257
284
private void StartCreatingNewSession ( )
258
285
{
259
286
ClearNewForm ( ) ;
287
+ // Ensure we have a fresh hosts list before we open the form.
288
+ SetAvailableHostsFromRpcModel ( _rpcController . GetState ( ) ) ;
260
289
CreatingNewSession = true ;
261
290
}
262
291
@@ -293,6 +322,8 @@ public async Task OpenLocalPathSelectDialog()
293
322
[ RelayCommand ]
294
323
public void OpenRemotePathSelectDialog ( )
295
324
{
325
+ if ( string . IsNullOrWhiteSpace ( NewSessionRemoteHost ) )
326
+ return ;
296
327
if ( _remotePickerWindow is not null )
297
328
{
298
329
_remotePickerWindow . Activate ( ) ;
@@ -304,6 +335,7 @@ public void OpenRemotePathSelectDialog()
304
335
pickerViewModel . PathSelected += OnRemotePathSelected ;
305
336
306
337
_remotePickerWindow = new DirectoryPickerWindow ( pickerViewModel ) ;
338
+ _remotePickerWindow . SetParent ( _window ) ;
307
339
_remotePickerWindow . Closed += ( _ , _ ) =>
308
340
{
309
341
_remotePickerWindow = null ;
@@ -347,7 +379,7 @@ await _syncSessionController.CreateSyncSession(new CreateSyncSessionRequest
347
379
Beta = new CreateSyncSessionRequest . Endpoint
348
380
{
349
381
Protocol = CreateSyncSessionRequest . Endpoint . ProtocolKind . Ssh ,
350
- Host = NewSessionRemoteHost ,
382
+ Host = NewSessionRemoteHost ! ,
351
383
Path = NewSessionRemotePath ,
352
384
} ,
353
385
} , cts . Token ) ;
0 commit comments