feat(app): copy-to-clipboard button for receive address

Adds a "Copy" button next to the receive address in the Receive tab.
Uses Avalonia's Clipboard API (async, code-behind) and calls
NotifyAddressCopied() to show the existing addr.copied status message.
New i18n key receive.copy in all 6 languages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 10:47:01 +02:00
parent 7727dbfddc
commit 4c7e8696cb
4 changed files with 23 additions and 2 deletions
+1
View File
@@ -116,6 +116,7 @@ public sealed class Loc
["tab.send"] = ["Invia", "Send", "Enviar", "Envoyer", "Enviar", "Senden"], ["tab.send"] = ["Invia", "Send", "Enviar", "Envoyer", "Enviar", "Senden"],
["tab.contacts"] = ["Contatti", "Contacts", "Contactos", "Contacts", "Contatos", "Kontakte"], ["tab.contacts"] = ["Contatti", "Contacts", "Contactos", "Contacts", "Contatos", "Kontakte"],
["receive.next"] = ["Prossimo indirizzo non usato:", "Next unused address:", "Próxima dirección no usada:", "Prochaine adresse non utilisée :", "Próximo endereço não usado:", "Nächste ungenutzte Adresse:"], ["receive.next"] = ["Prossimo indirizzo non usato:", "Next unused address:", "Próxima dirección no usada:", "Prochaine adresse non utilisée :", "Próximo endereço não usado:", "Nächste ungenutzte Adresse:"],
["receive.copy"] = ["Copia", "Copy", "Copiar", "Copier", "Copiar", "Kopieren"],
["receive.hint"] = [ ["receive.hint"] = [
"Ogni pagamento ricevuto qui comparirà nello storico alla prossima sincronizzazione.", "Ogni pagamento ricevuto qui comparirà nello storico alla prossima sincronizzazione.",
"Payments received here will appear in the history at the next synchronization.", "Payments received here will appear in the history at the next synchronization.",
@@ -254,6 +254,9 @@ public partial class MainWindowViewModel : ViewModelBase
previous?.Dispose(); previous?.Dispose();
} }
/// <summary>Feedback dopo la copia dell'indirizzo negli appunti (chiamato dal code-behind).</summary>
public void NotifyAddressCopied() => StatusMessage = Loc.Tr("addr.copied");
private static Bitmap? GenerateQr(string text) private static Bitmap? GenerateQr(string text)
{ {
try try
+8 -2
View File
@@ -253,8 +253,14 @@
<TabItem Header="{Binding Loc[tab.receive]}"> <TabItem Header="{Binding Loc[tab.receive]}">
<StackPanel Spacing="10" Margin="8"> <StackPanel Spacing="10" Margin="8">
<TextBlock Text="{Binding Loc[receive.next]}"/> <TextBlock Text="{Binding Loc[receive.next]}"/>
<SelectableTextBlock Text="{Binding ReceiveAddress}" <StackPanel Orientation="Horizontal" Spacing="8">
FontFamily="monospace" FontSize="16"/> <SelectableTextBlock Text="{Binding ReceiveAddress}"
FontFamily="monospace" FontSize="16"
VerticalAlignment="Center"/>
<Button Content="{Binding Loc[receive.copy]}"
Click="OnCopyReceiveAddressClick"
VerticalAlignment="Center"/>
</StackPanel>
<Border Background="White" Padding="12" CornerRadius="6" <Border Background="White" Padding="12" CornerRadius="6"
HorizontalAlignment="Left" HorizontalAlignment="Left"
IsVisible="{Binding ReceiveQr, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{Binding ReceiveQr, Converter={x:Static ObjectConverters.IsNotNull}}">
+11
View File
@@ -87,6 +87,17 @@ public partial class MainWindow : Window
vm.ApplyDataLocation(path); vm.ApplyDataLocation(path);
} }
private async void OnCopyReceiveAddressClick(object? sender, RoutedEventArgs e)
{
if (DataContext is not MainWindowViewModel vm || string.IsNullOrEmpty(vm.ReceiveAddress))
return;
if (Clipboard is { } clipboard)
{
await clipboard.SetTextAsync(vm.ReceiveAddress);
vm.NotifyAddressCopied();
}
}
private void OnConnectionStatusTapped(object? sender, TappedEventArgs e) private void OnConnectionStatusTapped(object? sender, TappedEventArgs e)
{ {
if (DataContext is MainWindowViewModel vm) if (DataContext is MainWindowViewModel vm)