Skip to content

feat: add create progress to file sync window #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions App/Services/MutagenController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public interface ISyncSessionController : IAsyncDisposable
/// </summary>
Task<SyncSessionControllerStateModel> RefreshState(CancellationToken ct = default);

Task<SyncSessionModel> CreateSyncSession(CreateSyncSessionRequest req, CancellationToken ct = default);
Task<SyncSessionModel> CreateSyncSession(CreateSyncSessionRequest req, Action<string> progressCallback, CancellationToken ct = default);
Task<SyncSessionModel> PauseSyncSession(string identifier, CancellationToken ct = default);
Task<SyncSessionModel> ResumeSyncSession(string identifier, CancellationToken ct = default);
Task TerminateSyncSession(string identifier, CancellationToken ct = default);
Expand Down Expand Up @@ -200,12 +200,15 @@ public async Task<SyncSessionControllerStateModel> RefreshState(CancellationToke
return state;
}

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

await using var prompter = await Prompter.Create(client, true, ct);
if (progressCallback != null)
prompter.OnProgress += (_, progress) => progressCallback(progress);

var createRes = await client.Synchronization.CreateAsync(new CreateRequest
{
Prompter = prompter.Identifier,
Expand Down Expand Up @@ -603,6 +606,8 @@ private async Task StopDaemon(CancellationToken ct)

private class Prompter : IAsyncDisposable
{
public event EventHandler<string>? OnProgress;

private readonly AsyncDuplexStreamingCall<HostRequest, HostResponse> _dup;
private readonly CancellationTokenSource _cts;
private readonly Task _handleRequestsTask;
Expand Down Expand Up @@ -684,6 +689,9 @@ private async Task HandleRequests(CancellationToken ct)
if (response.Message == null)
throw new InvalidOperationException("Prompting.Host response stream returned a null message");

if (!response.IsPrompt)
OnProgress?.Invoke(this, response.Message);

// Currently we only reply to SSH fingerprint messages with
// "yes" and send an empty reply for everything else.
var reply = "";
Expand Down
22 changes: 20 additions & 2 deletions App/ViewModels/FileSyncListViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public partial class FileSyncListViewModel : ObservableObject
public partial string NewSessionRemotePath { get; set; } = "";
// TODO: NewSessionRemotePathDialogOpen for remote path

[ObservableProperty]
public partial string NewSessionStatus { get; set; } = "";

public bool NewSessionCreateEnabled
{
get
Expand Down Expand Up @@ -187,6 +190,7 @@ private void ClearNewForm()
NewSessionLocalPath = "";
NewSessionRemoteHost = "";
NewSessionRemotePath = "";
NewSessionStatus = "";
}

[RelayCommand]
Expand Down Expand Up @@ -263,13 +267,26 @@ private void CancelNewSession()
ClearNewForm();
}

private void OnCreateSessionProgress(string message)
{
// Ensure we're on the UI thread.
if (_dispatcherQueue == null) return;
if (!_dispatcherQueue.HasThreadAccess)
{
_dispatcherQueue.TryEnqueue(() => OnCreateSessionProgress(message));
return;
}

NewSessionStatus = message;
}

[RelayCommand]
private async Task ConfirmNewSession()
{
if (OperationInProgress || !NewSessionCreateEnabled) return;
OperationInProgress = true;

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(120));
try
{
// The controller will send us a state changed event.
Expand All @@ -286,7 +303,7 @@ await _syncSessionController.CreateSyncSession(new CreateSyncSessionRequest
Host = NewSessionRemoteHost,
Path = NewSessionRemotePath,
},
}, cts.Token);
}, OnCreateSessionProgress, cts.Token);

ClearNewForm();
}
Expand All @@ -304,6 +321,7 @@ await _syncSessionController.CreateSyncSession(new CreateSyncSessionRequest
finally
{
OperationInProgress = false;
NewSessionStatus = "";
}
}

Expand Down
14 changes: 12 additions & 2 deletions App/Views/Pages/FileSyncListMainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,11 @@
<ColumnDefinition Width="2*" MinWidth="200" />
<ColumnDefinition Width="1*" MinWidth="120" />
<ColumnDefinition Width="2*" MinWidth="200" />
<ColumnDefinition Width="1*" MinWidth="100" MaxWidth="200" />
<ColumnDefinition Width="1*" MinWidth="100" MaxWidth="200" />
<!--
To fit the status better, the last two columns
are merged for the new sync row.
-->
<ColumnDefinition Width="2*" MinWidth="200" MaxWidth="400" />
</Grid.ColumnDefinitions>

<Border Grid.Column="0" Padding="0">
Expand Down Expand Up @@ -340,6 +343,13 @@
HorizontalAlignment="Stretch"
Text="{x:Bind ViewModel.NewSessionRemotePath, Mode=TwoWay}" />
</Border>
<Border Grid.Column="4">
<TextBlock
Text="{x:Bind ViewModel.NewSessionStatus, Mode=OneWay}"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"
IsTextTrimmedChanged="TooltipText_IsTextTrimmedChanged" />
</Border>
</Grid>
</StackPanel>
</ScrollView>
Expand Down
18 changes: 14 additions & 4 deletions Tests.App/Services/MutagenControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ public async Task Ok(CancellationToken ct)
// Ensure the daemon is stopped because all sessions are terminated.
await AssertDaemonStopped(dataDirectory, ct);

var progressMessages = new List<string>();
void OnProgress(string message)
{
TestContext.Out.WriteLine("Create session progress: " + message);
progressMessages.Add(message);
}

var session1 = await controller.CreateSyncSession(new CreateSyncSessionRequest
{
Alpha = new CreateSyncSessionRequest.Endpoint
Expand All @@ -124,7 +131,10 @@ public async Task Ok(CancellationToken ct)
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
Path = betaDirectory.FullName,
},
}, ct);
}, OnProgress, ct);

// There should've been at least one progress message.
Assert.That(progressMessages, Is.Not.Empty);

state = controller.GetState();
Assert.That(state.SyncSessions, Has.Count.EqualTo(1));
Expand All @@ -142,7 +152,7 @@ public async Task Ok(CancellationToken ct)
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
Path = betaDirectory.FullName,
},
}, ct);
}, null, ct);

state = controller.GetState();
Assert.That(state.SyncSessions, Has.Count.EqualTo(2));
Expand Down Expand Up @@ -225,7 +235,7 @@ await controller.CreateSyncSession(new CreateSyncSessionRequest
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
Path = betaDirectory.FullName,
},
}, ct);
}, null, ct);
}

await AssertDaemonStopped(dataDirectory, ct);
Expand Down Expand Up @@ -265,7 +275,7 @@ await controller1.CreateSyncSession(new CreateSyncSessionRequest
Protocol = CreateSyncSessionRequest.Endpoint.ProtocolKind.Local,
Path = betaDirectory.FullName,
},
}, ct);
}, null, ct);

controller2 = new MutagenController(_mutagenBinaryPath, dataDirectory);
await controller2.RefreshState(ct);
Expand Down
Loading