qml: allow renaming wallets

Allows to rename a wallet file from the QML Wallet Details view.
This seems like a feature we should support as the use-case of a wallet can
change or maybe the user didn't think about a proper name when setting
up the wallet. Especially with lightning channels it is not possible to
restore from seed to change the name.

Fixes #4377
This commit is contained in:
f321x
2026-04-08 10:03:14 +02:00
parent 7a6a39d1aa
commit 8a12874c8e
3 changed files with 106 additions and 0 deletions
+11
View File
@@ -585,6 +585,17 @@ class Daemon(Logger):
return True
return False
def rename_wallet_file(self, old_path: str, new_path: str):
old_path = standardize_path(old_path)
new_path = standardize_path(new_path)
if os.path.exists(new_path):
raise ValueError("Wallet file already exists")
os.rename(old_path, new_path)
self.logger.debug(f'renamed wallet: {old_path} -> {new_path}')
self.update_recently_opened_wallets(old_path, remove=True)
if self.config.CURRENT_WALLET == old_path:
self.config.CURRENT_WALLET = new_path
def stop_wallet(self, path: str) -> bool:
"""Returns True iff a wallet was found."""
assert util.get_running_loop() != util.get_asyncio_loop(), 'must not be called from asyncio thread'
@@ -130,6 +130,64 @@ Pane {
visible: Daemon.currentWallet
columns: 2
Label {
Layout.columnSpan: 2
Layout.topMargin: constants.paddingSmall
text: qsTr('Name')
color: Material.accentColor
}
TextHighlightPane {
id: walletNameContent
property bool editmode: false
Layout.columnSpan: 2
Layout.fillWidth: true
RowLayout {
width: parent.width
Label {
visible: !walletNameContent.editmode
text: Daemon.currentWallet.name
wrapMode: Text.Wrap
Layout.fillWidth: true
font.pixelSize: constants.fontSizeLarge
}
ToolButton {
visible: !walletNameContent.editmode
icon.source: '../../icons/pen.png'
icon.color: 'transparent'
onClicked: {
walletNameEdit.text = Daemon.currentWallet.name
walletNameContent.editmode = true
walletNameEdit.focus = true
}
}
TextField {
id: walletNameEdit
visible: walletNameContent.editmode
Layout.fillWidth: true
font.pixelSize: constants.fontSizeLarge
}
ToolButton {
visible: walletNameContent.editmode
icon.source: '../../icons/confirmed.png'
icon.color: enabled ? 'transparent' : constants.mutedForeground
enabled: walletNameEdit.text !== Daemon.currentWallet.name
&& Daemon.isValidWalletName(walletNameEdit.text)
onClicked: {
walletNameContent.editmode = false
Daemon.renameWallet(walletNameEdit.text)
}
}
ToolButton {
visible: walletNameContent.editmode
icon.source: '../../icons/closebutton.png'
icon.color: 'transparent'
onClicked: walletNameContent.editmode = false
}
}
}
Label {
Layout.columnSpan: 2
Layout.topMargin: constants.paddingSmall
@@ -526,6 +584,14 @@ Pane {
dialog.open()
}
}
function onWalletRenameError(message) {
var dialog = app.messageDialog.createObject(app, {
title: qsTr('Error'),
iconSource: Qt.resolvedUrl('../../icons/warning.png'),
text: message
})
dialog.open()
}
}
Connections {
+29
View File
@@ -150,6 +150,7 @@ class QEDaemon(AuthMixin, QObject):
walletRequiresPassword = pyqtSignal([str, str], arguments=['name', 'path'])
walletOpenError = pyqtSignal([str], arguments=["error"])
walletDeleteError = pyqtSignal([str, str], arguments=['code', 'message'])
walletRenameError = pyqtSignal([str], arguments=['message'])
def __init__(self, daemon: 'Daemon', plugins: 'Plugins', parent=None):
super().__init__(parent)
@@ -310,6 +311,34 @@ class QEDaemon(AuthMixin, QObject):
self.availableWallets.remove_wallet(path)
@pyqtSlot(str, result=bool)
def isValidWalletName(self, wallet_name: str) -> bool:
if not wallet_name:
return False
if self.availableWallets.wallet_name_exists(wallet_name):
return False
# ensure wallet_name is not interpreted as path
if os.path.basename(wallet_name) != wallet_name: # '/foo/bar/' returns 'bar'
return False
return True
@pyqtSlot(str)
def renameWallet(self, new_name: str):
wallet = self._current_wallet
assert wallet, "name change without wallet?"
old_path = standardize_path(wallet.wallet.storage.path)
wallet_dir = os.path.dirname(old_path)
new_path = standardize_path(os.path.join(wallet_dir, new_name))
if old_path == new_path:
return
self._current_wallet = None
self.daemon.stop_wallet(old_path)
try:
self.daemon.rename_wallet_file(old_path, new_path)
except Exception as e:
self.walletRenameError.emit(_('Error renaming wallet:\n') + str(e))
self.walletLoaded.emit(None, None)
@pyqtProperty(bool, notify=loadingChanged)
def loading(self):
return self._loading