Skip to content

Commit 413e13a

Browse files
committed
feat: add create progress to file sync window
1 parent bd221c4 commit 413e13a

File tree

4 files changed

+56
-10
lines changed

4 files changed

+56
-10
lines changed

App/Services/MutagenController.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public interface ISyncSessionController : IAsyncDisposable
8585
/// </summary>
8686
Task<SyncSessionControllerStateModel> RefreshState(CancellationToken ct = default);
8787

88-
Task<SyncSessionModel> CreateSyncSession(CreateSyncSessionRequest req, CancellationToken ct = default);
88+
Task<SyncSessionModel> CreateSyncSession(CreateSyncSessionRequest req, Action<string> progressCallback, CancellationToken ct = default);
8989
Task<SyncSessionModel> PauseSyncSession(string identifier, CancellationToken ct = default);
9090
Task<SyncSessionModel> ResumeSyncSession(string identifier, CancellationToken ct = default);
9191
Task TerminateSyncSession(string identifier, CancellationToken ct = default);
@@ -200,12 +200,15 @@ public async Task<SyncSessionControllerStateModel> RefreshState(CancellationToke
200200
return state;
201201
}
202202

203-
public async Task<SyncSessionModel> CreateSyncSession(CreateSyncSessionRequest req, CancellationToken ct = default)
203+
public async Task<SyncSessionModel> CreateSyncSession(CreateSyncSessionRequest req, Action<string>? progressCallback = null, CancellationToken ct = default)
204204
{
205205
using var _ = await _lock.LockAsync(ct);
206206
var client = await EnsureDaemon(ct);
207207

208208
await using var prompter = await Prompter.Create(client, true, ct);
209+
if (progressCallback != null)
210+
prompter.OnProgress += (_, progress) => progressCallback(progress);
211+
209212
var createRes = await client.Synchronization.CreateAsync(new CreateRequest
210213
{
211214
Prompter = prompter.Identifier,
@@ -603,6 +606,8 @@ private async Task StopDaemon(CancellationToken ct)
603606

604607
private class Prompter : IAsyncDisposable
605608
{
609+
public event EventHandler<string>? OnProgress;
610+
606611
private readonly AsyncDuplexStreamingCall<HostRequest, HostResponse> _dup;
607612
private readonly CancellationTokenSource _cts;
608613
private readonly Task _handleRequestsTask;
@@ -684,6 +689,9 @@ private async Task HandleRequests(CancellationToken ct)
684689
if (response.Message == null)
685690
throw new InvalidOperationException("Prompting.Host response stream returned a null message");
686691

692+
if (!response.IsPrompt)
693+
OnProgress?.Invoke(this, response.Message);
694+
687695
// Currently we only reply to SSH fingerprint messages with
688696
// "yes" and send an empty reply for everything else.
689697
var reply = "";

App/ViewModels/FileSyncListViewModel.cs

+20-2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public partial class FileSyncListViewModel : ObservableObject
6767
public partial string NewSessionRemotePath { get; set; } = "";
6868
// TODO: NewSessionRemotePathDialogOpen for remote path
6969

70+
[ObservableProperty]
71+
public partial string NewSessionStatus { get; set; } = "";
72+
7073
public bool NewSessionCreateEnabled
7174
{
7275
get
@@ -187,6 +190,7 @@ private void ClearNewForm()
187190
NewSessionLocalPath = "";
188191
NewSessionRemoteHost = "";
189192
NewSessionRemotePath = "";
193+
NewSessionStatus = "";
190194
}
191195

192196
[RelayCommand]
@@ -263,13 +267,26 @@ private void CancelNewSession()
263267
ClearNewForm();
264268
}
265269

270+
private void OnCreateSessionProgress(string message)
271+
{
272+
// Ensure we're on the UI thread.
273+
if (_dispatcherQueue == null) return;
274+
if (!_dispatcherQueue.HasThreadAccess)
275+
{
276+
_dispatcherQueue.TryEnqueue(() => OnCreateSessionProgress(message));
277+
return;
278+
}
279+
280+
NewSessionStatus = message;
281+
}
282+
266283
[RelayCommand]
267284
private async Task ConfirmNewSession()
268285
{
269286
if (OperationInProgress || !NewSessionCreateEnabled) return;
270287
OperationInProgress = true;
271288

272-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
289+
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(120));
273290
try
274291
{
275292
// The controller will send us a state changed event.
@@ -286,7 +303,7 @@ await _syncSessionController.CreateSyncSession(new CreateSyncSessionRequest
286303
Host = NewSessionRemoteHost,
287304
Path = NewSessionRemotePath,
288305
},
289-
}, cts.Token);
306+
}, OnCreateSessionProgress, cts.Token);
290307

291308
ClearNewForm();
292309
}
@@ -304,6 +321,7 @@ await _syncSessionController.CreateSyncSession(new CreateSyncSessionRequest
304321
finally
305322
{
306323
OperationInProgress = false;
324+
NewSessionStatus = "";
307325
}
308326
}
309327

App/Views/Pages/FileSyncListMainPage.xaml

+12-2
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,11 @@
274274
<ColumnDefinition Width="2*" MinWidth="200" />
275275
<ColumnDefinition Width="1*" MinWidth="120" />
276276
<ColumnDefinition Width="2*" MinWidth="200" />
277-
<ColumnDefinition Width="1*" MinWidth="100" MaxWidth="200" />
278-
<ColumnDefinition Width="1*" MinWidth="100" MaxWidth="200" />
277+
<!--
278+
To fit the status better, the last two columns
279+
are merged for the new sync row.
280+
-->
281+
<ColumnDefinition Width="2*" MinWidth="200" MaxWidth="400" />
279282
</Grid.ColumnDefinitions>
280283

281284
<Border Grid.Column="0" Padding="0">
@@ -340,6 +343,13 @@
340343
HorizontalAlignment="Stretch"
341344
Text="{x:Bind ViewModel.NewSessionRemotePath, Mode=TwoWay}" />
342345
</Border>
346+
<Border Grid.Column="4">
347+
<TextBlock
348+
Text="{x:Bind ViewModel.NewSessionStatus, Mode=OneWay}"
349+
VerticalAlignment="Center"
350+
TextTrimming="CharacterEllipsis"
351+
IsTextTrimmedChanged="TooltipText_IsTextTrimmedChanged" />
352+
</Border>
343353
</Grid>
344354
</StackPanel>
345355
</ScrollView>

Tests.App/Services/MutagenControllerTest.cs

+14-4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ public async Task Ok(CancellationToken ct)
112112
// Ensure the daemon is stopped because all sessions are terminated.
113113
await AssertDaemonStopped(dataDirectory, ct);
114114

115+
var progressMessages = new List<string>();
116+
void OnProgress(string message)
117+
{
118+
TestContext.Out.WriteLine("Create session progress: " + message);
119+
progressMessages.Add(message);
120+
}
121+
115122
var session1 = await controller.CreateSyncSession(new CreateSyncSessionRequest
116123
{
117124
Alpha = new CreateSyncSessionRequest.Endpoint
@@ -124,7 +131,10 @@ public async Task Ok(CancellationToken ct)
124131
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
125132
Path = betaDirectory.FullName,
126133
},
127-
}, ct);
134+
}, OnProgress, ct);
135+
136+
// There should've been at least one progress message.
137+
Assert.That(progressMessages, Is.Not.Empty);
128138

129139
state = controller.GetState();
130140
Assert.That(state.SyncSessions, Has.Count.EqualTo(1));
@@ -142,7 +152,7 @@ public async Task Ok(CancellationToken ct)
142152
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
143153
Path = betaDirectory.FullName,
144154
},
145-
}, ct);
155+
}, null, ct);
146156

147157
state = controller.GetState();
148158
Assert.That(state.SyncSessions, Has.Count.EqualTo(2));
@@ -225,7 +235,7 @@ await controller.CreateSyncSession(new CreateSyncSessionRequest
225235
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
226236
Path = betaDirectory.FullName,
227237
},
228-
}, ct);
238+
}, null, ct);
229239
}
230240

231241
await AssertDaemonStopped(dataDirectory, ct);
@@ -265,7 +275,7 @@ await controller1.CreateSyncSession(new CreateSyncSessionRequest
265275
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
266276
Path = betaDirectory.FullName,
267277
},
268-
}, ct);
278+
}, null, ct);
269279

270280
controller2 = new MutagenController(_mutagenBinaryPath, dataDirectory);
271281
await controller2.RefreshState(ct);

0 commit comments

Comments
 (0)