feat: rename hardware wallets#1057
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Greptile SummaryThis PR adds a rename flow for saved hardware wallets: tapping a wallet name in Settings → General → Hardware Wallets opens a bottom sheet pre-filled with the current label, lets the user edit it, and persists the change through
Confidence Score: 4/5The rename flow is well-structured and the repository layer correctly trims and persists the label. One edge case in the ViewModel — the save coroutine outliving a sheet dismissal — can cause a newly opened rename sheet to close without user action. The save coroutine in saveDeviceLabel is launched on viewModelScope and is never cancelled by onDismissRenameSheet. If the disk write completes after the user has dismissed one sheet and opened another, isRenameSaved is set to true in the shared state, which triggers LaunchedEffect(isRenameSaved) in the second sheet and closes it unexpectedly. While the window is narrow for a local disk operation, the fix is straightforward and the current code leaves an observable correctness gap. app/src/main/java/to/bitkit/ui/screens/wallets/HwWalletViewModel.kt — the save job lifecycle and the shared isRenameSaved signal.
|
| Filename | Overview |
|---|---|
| app/src/main/java/to/bitkit/ui/screens/wallets/HwWalletViewModel.kt | Adds rename state (isPendingRename, labelInput, isSavingLabel, isRenameSaved) and saveDeviceLabel logic; the save coroutine is not cancelled on sheet dismiss, leaving a stale isRenameSaved=true that can close a subsequently opened rename sheet. |
| app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt | Adds RenameHardwareWalletSheet and RenameHardwareWalletContent composables; wires up keyboard dismissal, IME padding, and save/dismiss flows correctly; uiState delegation refactored cleanly. |
| app/src/main/java/to/bitkit/ui/components/TextInput.kt | Initial cursor placement now defaults to end-of-text for all pre-filled inputs (TextRange(value.length)); consistent with the existing LaunchedEffect that already moved cursor to end on external value changes. |
| app/src/test/java/to/bitkit/ui/screens/wallets/HwWalletViewModelTest.kt | Good unit test coverage for the rename flow: open, input-cap, dismiss, save success, blank-label guard, and failure toast all tested. |
| app/src/main/res/values/strings.xml | Adds hardware__rename_error, settings__hardware_wallets__rename_title, and settings__hardware_wallets__name_label strings. |
| changelog.d/next/1056.added.md | Changelog entry for the rename feature. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant U as User
participant S as HardwareWalletsSettingsScreen
participant VM as HwWalletViewModel
participant Sheet as RenameHardwareWalletSheet
participant Repo as HwWalletRepo
U->>S: Tap wallet name
S->>VM: onRenameClick(wallet)
VM-->>S: "uiState(isPendingRename=wallet, labelInput=wallet.name)"
S->>Sheet: Compose RenameHardwareWalletSheet
U->>Sheet: Edit name input
Sheet->>VM: onLabelChange(value)
VM-->>Sheet: "uiState(labelInput=trimmed value)"
U->>Sheet: Tap Save
Sheet->>VM: saveDeviceLabel()
VM->>Repo: setDeviceLabel(wallet.id, labelInput)
Repo-->>VM: Result.success(Unit)
VM-->>Sheet: "uiState(isRenameSaved=true)"
Sheet->>Sheet: LaunchedEffect(isRenameSaved) fires
Sheet->>Sheet: hide sheet + dismissKeyboard
Sheet->>VM: onDismissRenameSheet()
VM-->>S: "uiState(isPendingRename=null)"
Note over Repo: setDeviceLabel trims label,<br/>updates hwWalletStore
Repo-->>S: wallets flow emits updated name
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant U as User
participant S as HardwareWalletsSettingsScreen
participant VM as HwWalletViewModel
participant Sheet as RenameHardwareWalletSheet
participant Repo as HwWalletRepo
U->>S: Tap wallet name
S->>VM: onRenameClick(wallet)
VM-->>S: "uiState(isPendingRename=wallet, labelInput=wallet.name)"
S->>Sheet: Compose RenameHardwareWalletSheet
U->>Sheet: Edit name input
Sheet->>VM: onLabelChange(value)
VM-->>Sheet: "uiState(labelInput=trimmed value)"
U->>Sheet: Tap Save
Sheet->>VM: saveDeviceLabel()
VM->>Repo: setDeviceLabel(wallet.id, labelInput)
Repo-->>VM: Result.success(Unit)
VM-->>Sheet: "uiState(isRenameSaved=true)"
Sheet->>Sheet: LaunchedEffect(isRenameSaved) fires
Sheet->>Sheet: hide sheet + dismissKeyboard
Sheet->>VM: onDismissRenameSheet()
VM-->>S: "uiState(isPendingRename=null)"
Note over Repo: setDeviceLabel trims label,<br/>updates hwWalletStore
Repo-->>S: wallets flow emits updated name
Comments Outside Diff (1)
-
app/src/main/java/to/bitkit/ui/components/TextInput.kt, line 52-69 (link)Global cursor-to-end on initial render — intentional but worth noting
Setting
selection = TextRange(value.length)in the initialremembermakes every pre-filledTextInputin the app start with the cursor at the end of the text. The existingLaunchedEffect(value)already did this for subsequent external updates, so this makes the initial state consistent with that. Just flagging it since it's a cross-cutting behavioral change for all callers — if any other usage intentionally pre-fills a value and expects the cursor at the start (e.g., a search field), it will now behave differently.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Reviews (1): Last reviewed commit: "chore: rename changelog fragment" | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bbf350d50a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-authored-by: Cursor <cursoragent@cursor.com>
Closes #1056
Description
This PR adds a Settings rename flow for saved hardware wallets.
It lets users tap a hardware wallet name from Settings -> General -> Hardware Wallets, edit the Bitkit-side display name in a bottom sheet, and save it through the existing hardware wallet label persistence. The saved label is reflected by the settings list and other hardware wallet surfaces because it continues to use the shared hardware wallet repository state.
Preview
Screen_recording_20260701_170947.mp4
QA Notes
Manual Tests
regression:BLE-paired hardware wallet -> rename from Settings: new name is saved and shown.regression:USB-paired hardware wallet -> rename from Settings: new name is saved and shown.Automated Checks
HwWalletViewModelTest.ktcovers opening rename state, input length cap, dismiss, blank-name guard, successful save state, and failure toast behavior.hardware-wallet.e2e.tscovers connecting, renaming, verifying, and removing a Trezor emulator wallet.just test file "to.bitkit.ui.screens.wallets.HwWalletViewModelTest"andjust lint.