v1.2.0: UX audit — security fixes, accessibility, and polish
Security (P0): - Fix sidebar remaining interactive behind lock screen - Extend auto-lock idle detection to include active widget interactions - Distinguish missing PIN vault from wrong PIN; auto-switch to passphrase Blocking UX (P1): - Add 15s timeout for encryption state check to prevent indefinite loading - Show restart reason in loading overlay after wallet encryption - Add Force Quit button on shutdown screen after 10s - Warn user if embedded daemon fails to start during wizard completion Polish (P2): - Use configured explorer URL in Receive tab instead of hardcoded URL - Increase request memo buffer from 256 to 512 bytes to match Send tab - Extend notification duration to 5s for critical operations (tx sent, wallet encrypted, key import, backup, export) - Add Reduce Motion accessibility setting (disables page fade + balance lerp) - Show estimated remaining time during mining thread benchmark - Add staleness indicator to market price data (warning after 5 min) New i18n keys: incorrect_pin, incorrect_passphrase, pin_not_set, restarting_after_encryption, force_quit, reduce_motion, tt_reduce_motion, ago, wizard_daemon_start_failed
This commit is contained in:
@@ -69,10 +69,13 @@ void App::encryptWalletWithPassphrase(const std::string& passphrase) {
|
||||
}
|
||||
|
||||
ui::Notifications::instance().info(
|
||||
"Wallet encrypted successfully");
|
||||
"Wallet encrypted successfully", 5.0f);
|
||||
|
||||
// The daemon shuts itself down after encryptwallet
|
||||
// The daemon shuts itself down after encryptwallet.
|
||||
// Update connection_status_ so the loading overlay
|
||||
// explains why the daemon is restarting.
|
||||
if (isUsingEmbeddedDaemon()) {
|
||||
connection_status_ = TR("restarting_after_encryption");
|
||||
// Give daemon a moment to shut down, then restart
|
||||
// (do this off the main thread to avoid stalling the UI)
|
||||
std::thread([this]() {
|
||||
@@ -164,13 +167,13 @@ void App::processDeferredEncryption() {
|
||||
if (pinStored) {
|
||||
settings_->setPinEnabled(true);
|
||||
settings_->save();
|
||||
ui::Notifications::instance().info("Wallet encrypted & PIN set");
|
||||
ui::Notifications::instance().info("Wallet encrypted & PIN set", 5.0f);
|
||||
} else {
|
||||
ui::Notifications::instance().warning(
|
||||
"Wallet encrypted but PIN vault failed");
|
||||
}
|
||||
} else {
|
||||
ui::Notifications::instance().info("Wallet encrypted successfully");
|
||||
ui::Notifications::instance().info("Wallet encrypted successfully", 5.0f);
|
||||
}
|
||||
|
||||
// Securely clear deferred state
|
||||
@@ -264,7 +267,7 @@ void App::unlockWallet(const std::string& passphrase, int timeout) {
|
||||
state_.unlocked_until = std::time(nullptr) + timeout;
|
||||
} else {
|
||||
lock_attempts_++;
|
||||
lock_error_msg_ = "Incorrect passphrase";
|
||||
lock_error_msg_ = TR("incorrect_passphrase");
|
||||
lock_error_timer_ = 3.0f;
|
||||
memset(lock_passphrase_buf_, 0, sizeof(lock_passphrase_buf_));
|
||||
|
||||
@@ -788,10 +791,17 @@ void App::renderLockScreen() {
|
||||
bool vaultOk = vault_ && vault_->retrieve(pin, passphrase);
|
||||
|
||||
if (!vaultOk) {
|
||||
return [this]() {
|
||||
bool noVault = !vault_ || !vault_->hasVault();
|
||||
return [this, noVault]() {
|
||||
lock_unlock_in_progress_ = false;
|
||||
lock_attempts_++;
|
||||
lock_error_msg_ = "Incorrect PIN";
|
||||
if (noVault) {
|
||||
// Vault file missing — switch to passphrase mode
|
||||
lock_error_msg_ = TR("pin_not_set");
|
||||
lock_use_pin_ = false;
|
||||
} else {
|
||||
lock_attempts_++;
|
||||
lock_error_msg_ = TR("incorrect_pin");
|
||||
}
|
||||
lock_error_timer_ = 3.0f;
|
||||
|
||||
float baseDelay = ui::schema::UI().drawElement("security", "lockout-base-delay").sizeOr(2.0f);
|
||||
|
||||
Reference in New Issue
Block a user