fix(app): the share dialog does not depend on the clipboard, and the UI suite leaves the clipboard alone

The fallback dialog copied the invitation to the clipboard before it showed
itself. The clipboard belongs to the whole desktop: when another program
held it the dialog never appeared, and under the UI suite the copy overwrote
whatever the person at the machine had copied. The copy is now best effort,
the dialog says whether it happened, and the UI fixture skips it.

Refs #163

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
This commit is contained in:
james.maesandClaude Fable 5.2 committed 2026-11-10 19:16:15 -06:00
1 parent 7ca752eab5
commit 124d081964
2 files changed
+38 -10

No files matched your search

+1 -1
View File
@@ -118,7 +118,7 @@ On launch, gclo shows a brief branded splash, then the **Quick Sync** connect ca
**Activity log:** View > Activity log… shows the tail of today's log and links to the logs folder. Logs live under `%LOCALAPPDATA%\KofTwentyTwo\gclo\logs`, one file per day, kept for 30 days; they record run parameters and per-repo failures and never contain your token (see [SECURITY.md](SECURITY.md)).
**Share:** Help > Share gclo by email… opens a new message in your email program with an invitation already written: what gclo is, where to download the latest release, how to install it, and where to send feedback. Type an address and send; gclo itself sends nothing. If no email program is set up, the text is copied to the clipboard and shown, to paste into a message by hand.
**Share:** Help > Share gclo by email… opens a new message in your email program with an invitation already written: what gclo is, where to download the latest release, how to install it, and where to send feedback. Type an address and send; gclo itself sends nothing. If no email program is set up, the text is shown and copied to the clipboard, to paste into a message by hand.
**Updates:** Help > Check for updates… downloads and applies the latest release for your channel. Self-update is only available in installed builds (not the portable zip or a local debug build).
+37 -9
View File
@@ -1168,8 +1168,9 @@ public sealed partial class MainWindow : Window
/// Help > Share gclo by email: hands an invitation (what gclo is, where to download
/// it, how to install it, where to send feedback) to the user's email program as a
/// new message with no recipient, so they type an address and send. gclo sends
/// nothing itself. When no email program takes it, the text goes to the clipboard
/// and is shown, to be pasted into a message by hand.
/// nothing itself. When no email program takes it, the text is shown, and copied
/// to the clipboard when the clipboard can be had, to be pasted into a message by
/// hand.
/// </summary>
private async void ShareMenuItem_Click(object sender, RoutedEventArgs e)
{
@@ -1186,11 +1187,14 @@ public sealed partial class MainWindow : Window
return;
}
_log.Info("Share: no email program took the invitation; it was copied to the clipboard and shown.");
var clipboard = new Windows.ApplicationModel.DataTransfer.DataPackage();
clipboard.SetText(invitation.AsPlainText);
Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(clipboard);
await DialogGuard.ShowAsync(NewShareDialog(invitation));
// The clipboard belongs to the whole desktop: the UI suite leaves it alone
// (it would overwrite what the person at the machine copied), and when
// another program holds it the text is still shown, to be copied by hand.
bool copied = !UiTestFixture.IsEnabled && TryCopyToClipboard(invitation.AsPlainText);
_log.Info(copied
? "Share: no email program took the invitation; it was copied to the clipboard and shown."
: "Share: no email program took the invitation; it was shown.");
await DialogGuard.ShowAsync(NewShareDialog(invitation, copied));
}
catch(Exception ex)
{
@@ -1201,8 +1205,30 @@ public sealed partial class MainWindow : Window
/// <summary>Puts text on the clipboard; false, with a line in the log, when the clipboard cannot be had.</summary>
private bool TryCopyToClipboard(string text)
{
try
{
var package = new Windows.ApplicationModel.DataTransfer.DataPackage();
package.SetText(text);
Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(package);
return true;
}
catch(Exception ex)
{
// Another program holds the clipboard: not worth failing the action for.
_log.Error($"Share: the invitation could not be copied to the clipboard ({ex.Message}).", ex);
return false;
}
}
/// <summary>The invitation as text to paste, for when no email program opened it.</summary>
private ContentDialog NewShareDialog(ShareInvitation invitation)
/// <param name="invitation">The invitation to show.</param>
/// <param name="copied">True when the text is on the clipboard already; the dialog says which it is.</param>
private ContentDialog NewShareDialog(ShareInvitation invitation, bool copied)
{
// AcceptsReturn before Text: a box that takes one line keeps only the first
// line of what it is given, which here would be the subject and nothing else.
@@ -1221,7 +1247,9 @@ public sealed partial class MainWindow : Window
var panel = new StackPanel { Spacing = 12 };
panel.Children.Add(new TextBlock
{
Text = "No email program opened the invitation. It has been copied to the clipboard: paste it into a new message, add an address, and send.",
Text = copied
? "No email program opened the invitation. It has been copied to the clipboard: paste it into a new message, add an address, and send."
: "No email program opened the invitation. Copy the text below into a new message, add an address, and send.",
TextWrapping = TextWrapping.Wrap,
});
panel.Children.Add(text);