From 37159e47a29f65017d0171071fa7b21876af8e19 Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 24 Mar 2026 14:24:29 +0100 Subject: [PATCH 1/2] qml: 2fa: make it possible to copy 2fa secret The 2fa secret is not selectable or copyable, this is very inconveniant when setting up a new 2fa wallet as the user has to somehow manually write the secret e.g. on a paper to then enter it again in their 2fa app. This makes the secret string copyable by clicking on it. --- .../trustedcoin/qml/ShowConfirmOTP.qml | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml b/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml index 778b3ddb6..b99ab97b6 100644 --- a/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml +++ b/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml @@ -42,13 +42,25 @@ WizardComponent { render: plugin.otpSecret } - TextHighlightPane { + Item { Layout.alignment: Qt.AlignHCenter visible: plugin.otpSecret - Label { - text: plugin.otpSecret - font.family: FixedFont - font.bold: true + implicitWidth: otpSecretPane.implicitWidth + implicitHeight: otpSecretPane.implicitHeight + TextHighlightPane { + id: otpSecretPane + Label { + text: plugin.otpSecret + font.family: FixedFont + font.bold: true + } + } + MouseArea { + anchors.fill: parent + onClicked: { + AppController.textToClipboard(plugin.otpSecret) + toaster.show(otpSecretPane, qsTr('Copied!')) + } } } @@ -118,6 +130,10 @@ WizardComponent { otp_auth.forceActiveFocus() } + Toaster { + id: toaster + } + Connections { target: plugin function onOtpError(message) { From cb023e22e4879d75a76217b6641c1a3618897e37 Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 24 Mar 2026 14:32:46 +0100 Subject: [PATCH 2/2] qml: 2fa: make 2fa setup qr code clickable This will make the 2fa app open when the user clicks on the qr code, much more convenient than manually copy pasting the secret. --- electrum/gui/qml/components/controls/QRImage.qml | 4 ++++ electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/electrum/gui/qml/components/controls/QRImage.qml b/electrum/gui/qml/components/controls/QRImage.qml index 46929ea7b..ce8ac7154 100644 --- a/electrum/gui/qml/components/controls/QRImage.qml +++ b/electrum/gui/qml/components/controls/QRImage.qml @@ -8,6 +8,8 @@ Item { property bool enableToggleText: false // if true, clicking the QR code shows the encoded text property bool isTextState: false // internal state, if the above is enabled + signal clicked() + property var _qrprops: QRIP.getDimensions(qrdata) width: r.width @@ -71,6 +73,8 @@ Item { onClicked: { if (enableToggleText) { root.isTextState = !root.isTextState + } else { + root.clicked() } } } diff --git a/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml b/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml index b99ab97b6..a63d8ecc3 100644 --- a/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml +++ b/electrum/plugins/trustedcoin/qml/ShowConfirmOTP.qml @@ -40,6 +40,16 @@ WizardComponent { qrdata: encodeURI('otpauth://totp/Electrum 2FA ' + wizard_data['wallet_name'] + '?secret=' + plugin.otpSecret + '&digits=6') render: plugin.otpSecret + onClicked: { + if (plugin.otpSecret) { + if (AppController.isAndroid()) { + Qt.openUrlExternally(qrdata) + } else { + AppController.textToClipboard(plugin.otpSecret) + toaster.show(this, qsTr('Copied!')) + } + } + } } Item { @@ -68,7 +78,7 @@ WizardComponent { Layout.fillWidth: true visible: !otpVerified && plugin.otpSecret wrapMode: Text.Wrap - text: qsTr('Enter or scan into authenticator app. Then authenticate below') + text: qsTr('Tap the QR code to open in your authenticator app, or scan it manually. Then authenticate below') } Label {