feat: unified message banner for all screens#604
Conversation
Add UX specification, technical architecture, and HTML mockup for the MessageBanner component that will replace the ~50 ad-hoc error/message display implementations across screens with a single reusable component. Key design decisions: - Per-screen MessageBanner with show()/set_message() API - All colors via DashColors (zero hardcoded Color32 values) - 4 severity levels: Error, Warning, Success, Info - Auto-dismiss for Success/Info (5s), persistent for Error/Warning - Follows Component Design Pattern conventions (private fields, builder, show) - No changes to BackendTask/TaskResult/AppState architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughA comprehensive refactor replaces per-instance message and error state management across the UI with a global, centralized MessageBanner system. A new MessageBanner component enables global banner storage in egui context, auto-dismissal, elapsed-time display, and per-instance usage. AppContext now holds an egui::Context reference. Screens transition from local message fields and display_message implementations to MessageBanner::set_global calls for centralizing user feedback. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Screen as Screen/UI
participant App as AppState
participant Banner as MessageBanner
participant EguiCtx as egui Context
participant Central as island_central_panel
User->>Screen: Trigger action (e.g., save, submit)
Screen->>App: Backend task or validation error
App->>Banner: MessageBanner::set_global(ctx, msg, type)
Banner->>EguiCtx: Store BannerState in context data
Central->>Banner: show_global(ui) renders all banners
Banner->>Central: Render banner with icon, text, dismiss
User->>Banner: Click dismiss or wait for auto-dismiss
Banner->>EguiCtx: Remove BannerState or mark TimedOut
Central->>Central: Banner disappears on next frame
sequenceDiagram
participant OldScreen as Old Screen (pre-refactor)
participant NewScreen as New Screen (post-refactor)
participant GlobalBanner as MessageBanner (global)
Note over OldScreen: message field<br/>error_message field<br/>per-instance UI
OldScreen->>OldScreen: display_message(msg, type)<br/>store in message/error_message<br/>render inline
Note over NewScreen: refresh_banner field<br/>no message field<br/>simplified status enum
NewScreen->>GlobalBanner: MessageBanner::set_global(ctx, msg, type)
GlobalBanner->>GlobalBanner: Store in egui context
Note over GlobalBanner: Rendering happens centrally<br/>in island_central_panel<br/>Auto-dismiss managed by banner
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (15)
src/ui/dashpay/qr_scanner.rs (1)
323-369:⚠️ Potential issue | 🟡 MinorImplement missing ScreenLike lifecycle methods.
This impl lacks
refresh/refresh_on_arrivalandchange_context, which are required for screens insrc/ui/**/*.rs. Please add the methods (even as no-ops) per the trait signature.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/dashpay/qr_scanner.rs` around lines 323 - 369, The QRScannerScreen impl of ScreenLike is missing the lifecycle methods required by the trait; add no-op implementations (or appropriate behavior) for refresh() or refresh_on_arrival() (whichever the trait defines) and change_context(&mut self, ctx: AppContext) to the impl block for QRScannerScreen so the trait is fully implemented; keep them minimal (empty bodies or a small update) matching the trait signatures used across src/ui/**/*.rs and reference the ScreenLike impl for QRScannerScreen so the compiler recognizes methods like refresh()/refresh_on_arrival() and change_context().src/ui/tokens/view_token_claims_screen.rs (1)
70-97:⚠️ Potential issue | 🟡 MinorImplement missing ScreenLike lifecycle methods.
This impl lacks
refresh/refresh_on_arrivalandchange_context, which are required for screens insrc/ui/**/*.rs. Please add the methods (even as no-ops) per the trait signature.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/view_token_claims_screen.rs` around lines 70 - 97, The ScreenLike impl for ViewTokenClaimsScreen is missing required lifecycle methods; add the missing methods refresh (or refresh_on_arrival, depending on the trait variant) and change_context to the impl so the struct fully implements ScreenLike. Locate the impl ScreenLike for ViewTokenClaimsScreen and add fn refresh(&mut self) { /* no-op */ } or fn refresh_on_arrival(&mut self) { /* no-op */ } as appropriate to your ScreenLike trait, plus fn change_context(&mut self, ctx: AppContext) { /* update/ignore ctx as needed */ } using the exact method names and signatures from the ScreenLike trait so the file compiles and behavior remains unchanged.src/ui/contracts_documents/register_contract_screen.rs (2)
341-377:⚠️ Potential issue | 🟡 MinorImplement missing ScreenLike lifecycle methods.
This impl lacks
refresh/refresh_on_arrivalandchange_context, which are required for screens insrc/ui/**/*.rs. Please add the methods (even as no-ops) per the trait signature.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/contracts_documents/register_contract_screen.rs` around lines 341 - 377, Add the missing ScreenLike lifecycle methods to the impl for RegisterDataContractScreen: implement refresh(&mut self, ctx: &egui::Context) and refresh_on_arrival(&mut self, ctx: &egui::Context) (they can be no-ops) and implement change_context(&mut self, ctx: ScreenContext) to satisfy the ScreenLike trait; place them alongside the existing display_message and display_task_result methods and follow the trait signatures used across other screens so the compiler recognizes the impl of ScreenLike for RegisterDataContractScreen.
67-73:⚠️ Potential issue | 🟡 MinorSurface wallet-selection errors during initialization.
get_selected_walletcan populateerror_message, but it’s currently ignored innew(). After removing per-screen error state, this makes initial wallet failures silent. Consider surfacing via the global banner.Proposed fix
let selected_wallet = if let Some(ref identity) = selected_qualified_identity { get_selected_wallet(identity, Some(app_context), None, &mut error_message) } else { None }; + if let Some(err) = error_message { + MessageBanner::set_global(app_context.egui_ctx(), &err, MessageType::Error); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/contracts_documents/register_contract_screen.rs` around lines 67 - 73, The initialization in new() calls get_selected_wallet(...) which can set error_message but currently ignores it; update new() to check error_message after the call to get_selected_wallet and, if Some(msg), surface it to the global/banner UI via the app_context (e.g., call the app_context or global banner enqueue/display method) so initial wallet-selection failures are visible; ensure you still return the selected_wallet as before but also forward the error string to the global banner handling function.src/ui/tools/grovestark_screen.rs (1)
984-1003:⚠️ Potential issue | 🟡 MinorAdd missing
change_contextimplementation.This screen already implements
refreshandrefresh_on_arrival, butchange_contextis still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tools/grovestark_screen.rs` around lines 984 - 1003, Add the missing change_context method to GroveSTARKScreen to satisfy the ScreenLike trait: implement fn change_context(&mut self, new_context: AppContext) (or the exact signature used in ScreenLike) to update the screen's stored app_context (and any dependent state) and trigger necessary updates (e.g., call self.refresh() or self.refresh_on_arrival() as appropriate); locate GroveSTARKScreen and implement change_context to replace/clone the existing app_context field and then call the existing refresh/refresh_identities(&app_context)/refresh_contracts(&app_context) flow so the screen reflects the new context.src/ui/tools/platform_info_screen.rs (1)
141-225:⚠️ Potential issue | 🟡 MinorAdd missing
change_contextimplementation.This screen already implements
refreshandrefresh_on_arrival, butchange_contextis still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tools/platform_info_screen.rs` around lines 141 - 225, Add the missing change_context implementation in the impl ScreenLike for PlatformInfoScreen: implement fn change_context(&mut self, app_context: Arc<AppContext>) so it replaces the screen's AppContext (self.app_context = app_context), updates dependent cached fields (e.g. self.network = self.app_context.network), and clears or resets transient UI state such as self.platform_version, self.core_chain_lock_height, self.current_result, self.current_result_title and self.active_tasks; ensure the method signature and ownership match the ScreenLike trait definition used elsewhere.src/ui/tokens/direct_token_purchase_screen.rs (1)
346-387:⚠️ Potential issue | 🟡 MinorAdd missing
change_contextimplementation.This screen implements
refresh, butchange_contextis still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/direct_token_purchase_screen.rs` around lines 346 - 387, Implement the missing ScreenLike::change_context(&mut self, ctx: &AppContext) for PurchaseTokenScreen: add a method named change_context that updates the screen's stored app context reference/handle (the same field used elsewhere, e.g., self.app_context), clears or resets transient UI state such as self.refresh_banner and any error status, and then triggers the same refresh behavior used on arrival (call the existing refresh() or refresh_on_arrival() path used by other screens). Follow the pattern from other screens implementing ScreenLike so the screen's fields (fetched_pricing_schedule, pricing_fetch_attempted, status, refresh_banner) are correctly reinitialized when context changes.src/ui/mod.rs (1)
837-846: 🛠️ Refactor suggestion | 🟠 MajorAdd
change_context()method to ScreenLike trait.The trait is missing
change_context()required by the coding guidelines. Add it as a default no-op and delegate in theScreenimpl to match the pattern used for other optional methods.Suggested changes
pub trait ScreenLike { fn refresh(&mut self) {} fn refresh_on_arrival(&mut self) { self.refresh() } fn ui(&mut self, ctx: &Context) -> AppAction; fn display_message(&mut self, _message: &str, _message_type: MessageType) {} fn display_task_result(&mut self, _backend_task_success_result: BackendTaskSuccessResult) {} + fn change_context(&mut self, _app_context: Arc<AppContext>) {} fn pop_on_success(&mut self) {} }impl ScreenLike for Screen { + fn change_context(&mut self, app_context: Arc<AppContext>) { + Screen::change_context(self, app_context); + } fn refresh(&mut self) { match self { Screen::IdentitiesScreen(screen) => screen.refresh(), // ... }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/mod.rs` around lines 837 - 846, The ScreenLike trait needs a new optional method change_context() following the same pattern as the other no-op methods: add fn change_context(&mut self) {} to the ScreenLike trait as a default no-op, and in the Screen impl add a corresponding pub fn change_context(&mut self) { self.screen.change_context(); } (or delegate from whatever wrapper method name you use) so the Screen wrapper simply forwards change_context to the inner ScreenLike implementation.src/ui/wallets/send_screen.rs (1)
2580-2723:⚠️ Potential issue | 🟡 MinorAdd change_context to ScreenLike impl.
This ScreenLike impl still lacks
change_context(). Please add it to meet the screen contract.As per coding guidelines,
src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/wallets/send_screen.rs` around lines 2580 - 2723, The ScreenLike implementation for WalletSendScreen is missing the required change_context() method; add a public impl method fn change_context(&mut self, ctx: &AppContext) { ... } (or matching your trait signature) to the impl ScreenLike for WalletSendScreen so the type fulfills the ScreenLike contract. Locate the impl block containing ui(), display_message(), display_task_result(), refresh_on_arrival(), and refresh(), and add change_context() alongside those methods, updating any internal state (e.g., self.app_context or selected_wallet) as your trait expects.src/ui/tools/transition_visualizer_screen.rs (1)
386-431:⚠️ Potential issue | 🟡 MinorAdd change_context to ScreenLike impl.
This ScreenLike impl still lacks
change_context(). Please add it to meet the screen contract.As per coding guidelines,
src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tools/transition_visualizer_screen.rs` around lines 386 - 431, The impl ScreenLike for TransitionVisualizerScreen is missing the required change_context method; add a method named change_context to this impl with the exact signature defined by the ScreenLike trait (e.g., fn change_context(&mut self, ctx: &mut AppContext) or the crate::ui equivalent) and implement it to update the screen's internal state from the provided context (similar to how ui()/refresh()/display_message()/display_task_result() operate), placing it inside the impl block for TransitionVisualizerScreen so the type fully satisfies the ScreenLike contract.src/ui/dashpay/send_payment.rs (1)
379-449:⚠️ Potential issue | 🟡 MinorAdd change_context to ScreenLike impl.
This ScreenLike impl still lacks
change_context(). Please add it to meet the screen contract.As per coding guidelines,
src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/dashpay/send_payment.rs` around lines 379 - 449, The impl for ScreenLike on SendPaymentScreen is missing the required change_context method; add a change_context implementation matching the ScreenLike trait signature (same params/borrow patterns as other screens) inside the impl for SendPaymentScreen, update the internal app context/state (self.app_context or related fields) from the new context, and call any necessary refresh logic like self.load_contact_info() or self.refresh() so the screen updates when the app context changes; reference: implement change_context for SendPaymentScreen within the existing impl ScreenLike block so it mirrors other screens' behavior.src/ui/tokens/resume_tokens_screen.rs (1)
278-307:⚠️ Potential issue | 🟡 MinorAdd change_context to ScreenLike impl.
This ScreenLike impl doesn’t provide
change_context(). Please add it to align with the screen contract.As per coding guidelines,
src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/resume_tokens_screen.rs` around lines 278 - 307, Add the missing ScreenLike::change_context implementation for ResumeTokensScreen: implement fn change_context(&mut self, ctx: ScreenContext) (matching the ScreenLike trait) that updates the screen's stored context (assign ctx to the screen's context field, e.g., self.context) and then trigger the appropriate refresh behavior (call refresh_on_arrival() if present else refresh()) so the screen state is updated when context changes; ensure the method lives in the same impl block for ResumeTokensScreen alongside display_message/display_task_result/refresh.src/ui/contracts_documents/update_contract_screen.rs (2)
362-398:⚠️ Potential issue | 🟡 MinorAdd change_context to ScreenLike impl.
This ScreenLike impl still lacks
change_context(). Please add it to meet the screen contract.As per coding guidelines,
src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/contracts_documents/update_contract_screen.rs` around lines 362 - 398, The impl for ScreenLike on UpdateDataContractScreen is missing the required change_context method; add a change_context implementation matching the ScreenLike trait signature (change_context(...)) inside the impl block for UpdateDataContractScreen, and have it update the screen's context or call the existing refresh/refresh_on_arrival logic (e.g., update any context-related field on UpdateDataContractScreen and trigger refresh behavior) so the type satisfies the ScreenLike contract.
35-44:⚠️ Potential issue | 🟠 MajorBroadcastError blocks retry because the parsed contract is discarded.
After an error,
BroadcastStatus::BroadcastErroris set and theValidContractpayload is lost, so the Update button never reappears unless the user edits the JSON to re-parse. Consider rehydrating the parsed contract (or resetting to a retryable state) when a broadcast error occurs.✅ Suggested fix (rehydrate for retry)
- } else { - self.broadcast_status = BroadcastStatus::BroadcastError; - } + } else { + // Re-parse to restore ValidContract so the user can retry without editing JSON. + self.parse_contract(); + }Also applies to: 186-222, 364-370
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/contracts_documents/update_contract_screen.rs` around lines 35 - 44, The BroadcastError path currently drops the parsed contract (BroadcastStatus::BroadcastError) so retries are blocked; preserve the parsed contract by changing the enum (in src/ui/contracts_documents/update_contract_screen.rs) to carry the contract (e.g., BroadcastError(Box<DataContract>, Option<String>) or add a new variant RetryableBroadcastError(Box<DataContract>, Option<String>)), update all places that set BroadcastStatus::BroadcastError (and any match arms handling it) to supply the parsed contract and error message, and update the UI logic that decides whether to show the Update button to treat the new/updated BroadcastError variant as retryable (equivalent to ValidContract) so the user can retry without re-parsing.src/ui/identities/keys/add_key_screen.rs (1)
410-413:⚠️ Potential issue | 🟡 MinorDuplicate unreachable
Completecheck.Lines 402-405 already check
AddKeyStatus::Completeand return early. This second check at lines 410-413 is dead code.🧹 Remove duplicate check
ui.heading("Add New Key"); ui.add_space(10.0); - if self.add_key_status == AddKeyStatus::Complete { - inner_action |= self.show_success(ui); - return inner_action; - } - if self.selected_wallet.is_some()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/identities/keys/add_key_screen.rs` around lines 410 - 413, Remove the duplicate unreachable check for AddKeyStatus::Complete: the code block that tests `if self.add_key_status == AddKeyStatus::Complete { inner_action |= self.show_success(ui); return inner_action; }` is redundant because an earlier return already handles `AddKeyStatus::Complete`; delete this second check (the duplicate `Complete` branch) so `self.add_key_status` is only handled once and flow continues to the intended subsequent logic (leave `show_success` and `inner_action` usage only in the original place where it's returned).
🟡 Minor comments (11)
src/ui/identities/transfer_screen.rs-502-510 (1)
502-510:⚠️ Potential issue | 🟡 MinorAdd missing
change_contextimplementation.This screen implements
refresh, butchange_contextis still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/identities/transfer_screen.rs` around lines 502 - 510, The TransferScreen impl for ScreenLike is missing the required change_context(&mut self, ctx: ScreenContext) method; add a change_context implementation inside impl ScreenLike for TransferScreen that matches the ScreenLike trait signature (change_context), update TransferScreen's internal context/state as appropriate (e.g., store the incoming ScreenContext or update fields used by ui()/refresh()/refresh_on_arrival()), and trigger any necessary side-effects such as scheduling a refresh or clearing/setting refresh_banner similar to display_message/refresh behavior so the screen reacts to context changes consistently.src/ui/tokens/update_token_config.rs-918-934 (1)
918-934:⚠️ Potential issue | 🟡 MinorImplement missing ScreenLike lifecycle methods.
This impl lacks
refresh/refresh_on_arrivalandchange_context, which are required for screens insrc/ui/**/*.rs. Please add the methods (even as no-ops) per the trait signature.
As per coding guidelines:src/ui/**/*.rs: All screens must implement theScreenLiketrait with methods:ui(),display_task_result(),display_message(),refresh()/refresh_on_arrival(), andchange_context().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/update_token_config.rs` around lines 918 - 934, The impl for UpdateTokenConfigScreen is missing the ScreenLike lifecycle methods refresh/refresh_on_arrival and change_context; add no-op implementations that match the ScreenLike trait signatures (i.e., implement refresh() and/or refresh_on_arrival() and change_context() for UpdateTokenConfigScreen) alongside the existing display_message and display_task_result methods so the type fully implements ScreenLike. Ensure the method names and signatures exactly match the trait used by other screens (use those files as reference) and keep bodies empty or performing no-ops consistent with coding guidelines.src/ui/dashpay/qr_scanner.rs-50-58 (1)
50-58:⚠️ Potential issue | 🟡 MinorClear stale parsed QR data when input is empty.
If the user clears the input and clicks “Parse QR Code,”
parsed_qr_dataremains from a previous parse, so “Add Contact” can send outdated data. Reset it before returning.Proposed fix
if self.qr_data_input.is_empty() { + self.parsed_qr_data = None; crate::ui::components::MessageBanner::set_global( self.app_context.egui_ctx(), "Please enter QR code data", MessageType::Error, ); return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/ui/dashpay/qr_scanner.rs` around lines 50 - 58, In parse_qr_code, when self.qr_data_input is empty you currently show an error and return but do not clear stale parsed_qr_data; update parse_qr_code to reset self.parsed_qr_data to None (or the empty/default value used by the struct) before returning so the previous parsed QR payload cannot be reused by the "Add Contact" flow; locate the parsed_qr_data field on the same struct and clear it at the top of parse_qr_code right before the MessageBanner and return.docs/ai-design/2026-02-17-unified-messages/message-banner-ux-spec.md-31-35 (1)
31-35:⚠️ Potential issue | 🟡 MinorAdd language identifiers to fenced code blocks (MD040).
This resolves markdownlint warnings and improves formatting consistency.💡 Suggested fix
-``` +```text +-----------------------------------------------------------------------+ | [Icon] Message text here [5s] [x] | +-----------------------------------------------------------------------+ -``` +``` -``` +```text +--------------------------------------------------+ | Top Panel (header / navigation) | +--------------------------------------------------+ | Left Panel | [ Banner 1 ] | | | [ Banner 2 ] | | | +----- Screen Content -----+ | | | | ... | | | | +--------------------------+ | +--------------------------------------------------+ -``` +``` </details> Also applies to: 81-91 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/ai-design/2026-02-17-unified-messages/message-banner-ux-spec.mdaround
lines 31 - 35, Add language identifiers ("text") to the fenced code blocks for
the ASCII UI art examples so markdownlint MD040 is satisfied; update the three
backtick fences shown in the snippet (the banner box block and the larger layout
block—the same pattern also appears later around the second example) to use
text instead ofand ensure each opening fence has a matching closing
fence with no extra characters removed or added.</details> </blockquote></details> <details> <summary>docs/ai-design/2026-02-17-unified-messages/architecture.md-127-139 (1)</summary><blockquote> `127-139`: _⚠️ Potential issue_ | _🟡 Minor_ **Add language identifiers to fenced code blocks (MD040).** This clears markdownlint warnings and improves syntax highlighting. <details> <summary>💡 Suggested fix</summary> ```diff -``` +```rust render_banner(ui, text, message_type, annotation: Option<&str>) -> bool (dismissed?) ``` -``` +```text +-----------------------------------------------------------------------+ | [Icon] Message text here [5s] [x] | +-----------------------------------------------------------------------+ -``` +``` -``` +```text TaskResult::Error(message) → MessageBanner::set_global(ctx, &message, MessageType::Error) → screen.display_message(&message, MessageType::Error) // for side-effects ``` </details> Also applies to: 156-164 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/ai-design/2026-02-17-unified-messages/architecture.mdaround lines 127
- 139, Add explicit language identifiers to the fenced code blocks in the doc
snippets to satisfy MD040: update the first function block to userust for the render_banner signature (render_banner/ui/process_banner references), change the ASCII art and flow blocks totext (the visual structure box and the
TaskResult::Error → MessageBanner::set_global → screen.display_message
sequence), and ensure the other similar block at lines 156-164 also has a
language identifier; no code changes required, only annotate the three fenced
blocks with appropriate language tags.</details> </blockquote></details> <details> <summary>src/ui/tokens/tokens_screen/keyword_search.rs-63-70 (1)</summary><blockquote> `63-70`: _⚠️ Potential issue_ | _🟡 Minor_ **Elapsed timer can carry over across searches.** `MessageBanner::set_global` deduplicates by text, so repeating the same “Searching contracts...” banner can reuse the old `created_at` and the elapsed counter won’t reset on a new search. Consider clearing/replacing the prior banner handle before starting a new search. <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/tokens_screen/keyword_search.rs` around lines 63 - 70, The elapsed timer can persist because MessageBanner::set_global deduplicates by text; before creating a new banner in keyword_search (where you call MessageBanner::set_global and assign to self.operation_banner), clear or remove any existing banner handle stored in self.operation_banner (e.g., if let Some(prev) = self.operation_banner.take() { prev.close() } or call the banner's removal API) so the old banner is removed and the new handle gets a fresh created_at; then call MessageBanner::set_global and store the new handle in self.operation_banner as you already do. ``` </details> </blockquote></details> <details> <summary>src/ui/tokens/pause_tokens_screen.rs-91-117 (1)</summary><blockquote> `91-117`: _⚠️ Potential issue_ | _🟡 Minor_ **Fix “Burning” wording in pause authorization errors.** These messages are shown on the Pause screen and should say “pause,” not “burn.” <details> <summary>Suggested text fix</summary> ```diff - set_error_banner("Burning is not allowed on this token"); + set_error_banner("Pausing is not allowed on this token"); ... - "You are not allowed to burn this token. Only the contract owner is.", + "You are not allowed to pause this token. Only the contract owner is.", ... - set_error_banner("You are not allowed to burn this token"); + set_error_banner("You are not allowed to pause this token"); ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/pause_tokens_screen.rs` around lines 91 - 117, Update the user-facing error strings in the pause authorization checks to refer to "pause" instead of "burn": modify the messages passed to set_error_banner in the AuthorizedActionTakers match arms (the "Burning is not allowed on this token", "You are not allowed to burn this token", and "You are not allowed to burn this token. Only the contract owner is.") so they read e.g. "Pausing is not allowed on this token", "You are not allowed to pause this token", and "You are not allowed to pause this token. Only the contract owner is." Reference set_error_banner, identity_token_info, and the AuthorizedActionTakers::NoOne/ContractOwner/Identity branches to locate and change the strings. ``` </details> </blockquote></details> <details> <summary>src/ui/tokens/resume_tokens_screen.rs-279-286 (1)</summary><blockquote> `279-286`: _⚠️ Potential issue_ | _🟡 Minor_ **Handle both Error and Warning messages to prevent stuck "resuming" state.** The `display_message` method currently only clears `refresh_banner` and sets error state for `Error` messages. However, `Warning` messages are also emitted in the UI (they don't auto-dismiss in the banner), and should be treated identically to prevent the "Resuming tokens..." banner from continuing indefinitely. This pattern is already established across multiple similar screen implementations (send_screen, add_contracts_screen, add_token_by_id_screen, etc.) which all handle both `Error` and `Warning` variants together. <details> <summary>✅ Suggested change</summary> ```diff - if let MessageType::Error = message_type { + if matches!(message_type, MessageType::Error | MessageType::Warning) { if let Some(h) = self.refresh_banner.take() { h.clear(); } self.status = ResumeTokensStatus::Error; } ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/resume_tokens_screen.rs` around lines 279 - 286, The display_message method only handles MessageType::Error; update it to treat MessageType::Warning the same way so warning banners also clear and the UI doesn't stay in "Resuming tokens..." state—locate the display_message function and change the conditional on MessageType (e.g., use a match or matches!() to check for MessageType::Error or MessageType::Warning), then keep the existing behavior: take and clear self.refresh_banner (if Some) and set self.status = ResumeTokensStatus::Error. ``` </details> </blockquote></details> <details> <summary>src/ui/contracts_documents/contracts_documents_screen.rs-213-218 (1)</summary><blockquote> `213-218`: _⚠️ Potential issue_ | _🟡 Minor_ **Clear any active query banner on parse errors.** If a user submits an invalid query while a previous “Querying documents…” banner is active, the stale banner remains visible. Clearing it here avoids misleading UI state. <details> <summary>🐛 Suggested fix</summary> ```diff Err(e) => { + if let Some(h) = self.query_banner.take() { + h.clear(); + } self.document_query_status = DocumentQueryStatus::Error; MessageBanner::set_global( ui.ctx(), &format!("Failed to parse query properly: {}", e), MessageType::Error, ); } ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/ui/contracts_documents/contracts_documents_screen.rs` around lines 213 - 218, On parse error update, clear any existing global banner before setting the error banner to avoid leaving the prior "Querying documents..." message visible: in the block where you set self.document_query_status = DocumentQueryStatus::Error and call MessageBanner::set_global(ui.ctx(), ...), first call the MessageBanner method that removes/clears the global banner (e.g., MessageBanner::clear_global(ui.ctx()) or the project's equivalent) and then set the new error banner via MessageBanner::set_global; keep the same context ui.ctx() and the formatted error text. ``` </details> </blockquote></details> <details> <summary>src/ui/dashpay/contacts_list.rs-910-913 (1)</summary><blockquote> `910-913`: _⚠️ Potential issue_ | _🟡 Minor_ **Show a user-facing error banner when hide/unhide fails.** Right now failures only log, so the user gets no feedback. Consider showing a global error banner in addition to logging. <details> <summary>🐛 Suggested fix</summary> ```diff +use crate::ui::components::MessageBanner; ``` ```diff if let Some(identity) = &self.selected_identity { let owner_id = identity.identity.id(); if let Err(e) = self.app_context.db.set_contact_hidden( &owner_id, &contact.identity_id, new_hidden, ) { + MessageBanner::set_global( + ui.ctx(), + &format!("Failed to update contact: {}", e), + MessageType::Error, + ); tracing::error!( "Failed to update contact: {}", e ); } else { ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/ui/dashpay/contacts_list.rs` around lines 910 - 913, The error handler that currently only calls tracing::error!("Failed to update contact: {}", e) should also surface a user-facing global error banner: keep the tracing::error log, then call or create a UI helper (e.g. show_error_banner or AppState::show_global_error) to display a concise message like "Failed to hide/unhide contact" plus the error details or a short code; do this in the same error branch where the variable e is available so the user sees feedback when the hide/unhide operation fails. ``` </details> </blockquote></details> <details> <summary>src/ui/tokens/unfreeze_tokens_screen.rs-115-132 (1)</summary><blockquote> `115-132`: _⚠️ Potential issue_ | _🟡 Minor_ **Update banner copy to match “unfreeze” semantics.** These messages still reference “burning,” which is misleading in the unfreeze flow. <details> <summary>Proposed wording fix</summary> ```diff - set_error_banner("Burning is not allowed on this token"); + set_error_banner("Unfreezing is not allowed on this token"); ``` ```diff - "You are not allowed to burn this token. Only the contract owner is.", + "You are not allowed to unfreeze this token. Only the contract owner is.", ``` ```diff - set_error_banner("You are not allowed to burn this token"); + set_error_banner("You are not allowed to unfreeze this token"); ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/ui/tokens/unfreeze_tokens_screen.rs` around lines 115 - 132, The error banners in the AuthorizedActionTakers match arms (variants NoOne, ContractOwner, Identity) use "burn" language but this flow is for unfreezing; update calls to set_error_banner to use unfreeze wording (e.g., "Unfreezing is not allowed on this token", "You are not allowed to unfreeze this token", and for ContractOwner: "You are not allowed to unfreeze this token. Only the contract owner is.") while keeping the same guard checks against identity_token_info and without changing control flow in those branches. ``` </details> </blockquote></details> </blockquote></details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
* refactor(context): replace RwLock<Sdk> with ArcSwap<Sdk> Sdk is internally thread-safe (Arc, ArcSwapOption, atomics) and all methods take &self. The RwLock was adding unnecessary contention across backend tasks. Using ArcSwap instead of plain Sdk because reinit_core_client_and_sdk() needs to atomically swap the entire Sdk instance when config changes. ArcSwap provides lock-free reads with atomic swap for the rare write. Suggested-by: lklimek * fix: address CodeRabbit review findings for ArcSwap migration - Fix import ordering: move arc_swap::ArcSwap before crossbeam_channel - Remove redundant SDK loads in load_identity_from_wallet, register_dpns_name, and load_identity — use the sdk parameter already passed to these functions - Fix stale TODO referencing removed sdk.read().unwrap() API - Rename sdk_guard → sdk in transfer, withdraw_from_identity, and refresh_loaded_identities_dpns_names (no longer lock guards) - Pass &sdk to run_platform_info_task from dispatch site instead of reloading internally - Fix leftover sdk.write() call in context_provider.rs (RwLock remnant) - Add missing Color32 import in wallets dialogs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: address remaining CodeRabbit review feedback on ArcSwap migration - Move SDK load outside for loop in refresh_loaded_identities_dpns_names.rs so it's loaded once for the batch instead of on each iteration - Update stale TODO comment in default_platform_version() to reflect that this is a free function with no sdk access Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: consolidate double read-lock on spv_context_provider Clone the SPV provider in a single lock acquisition, then bind app context on the clone instead of locking twice. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: remove unused Insight API references The `insight_api_url` field in `NetworkConfig` and its associated `insight_api_uri()` method were never used in production code (both marked `#[allow(dead_code)]`). Remove the field, method, config entries, env example lines, and related tests. https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn * refactor: remove unused `show_in_ui` field from NetworkConfig The `show_in_ui` field was defined on `NetworkConfig` and serialized in `save()`, but never read by any production code to control network visibility. Remove the field, its serialization, env example lines, and test references. https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn * fix: add missing `Color32` import in wallet dialogs https://claude.ai/code/session_01HWPmCJHT8KTZGP9bFiksjn --------- Co-authored-by: Claude <noreply@anthropic.com>
* build: remove snap version * build: add Flatpak packaging and CI workflow Add Flatpak build manifest, desktop entry, AppStream metadata, and GitHub Actions workflow for building and distributing Flatpak bundles. Uses freedesktop 25.08 runtime with rust-stable and llvm21 extensions. No application source code changes required - works in SPV mode by default. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: address review findings for Flatpak packaging - Pin GitHub Actions to commit SHAs for supply chain security - Upgrade softprops/action-gh-release from v1 to v2.2.2 - Remove redundant --socket=x11 (fallback-x11 suffices) - Remove duplicate tag trigger preventing double builds on release - Remove duplicate env vars inherited from top-level build-options - Add Flatpak build artifacts to .gitignore - Add bugtracker URL to AppStream metainfo - Remove deprecated <categories> from metainfo (use .desktop instead) - Add Terminal=false and Keywords to desktop entry - Add disk space check after SDK install in CI - Rename artifact to include architecture suffix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: simplify CI workflows for Linux-only releases - Remove "Free disk space" step from flatpak and release workflows - Remove Windows and macOS builds from release workflow - Use "ubuntu" runner image instead of pinned versions - Clean up unused matrix.ext references Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: attach to existing releases instead of creating new ones - Replace release-creating job with attach-to-release (only on release event) - Add 14-day retention for build artifacts - On tag push or workflow_dispatch, only upload artifacts (no release) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * revert: restore release.yml to original v1.0-dev version The release workflow changes were out of scope for the Flatpak PR. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review comments - Fix CRLF line endings in Flatpak manifest (convert to LF) - Set app_id on ViewportBuilder to match desktop StartupWMClass - Use --locked flag for reproducible cargo builds in Flatpak - Rename --repo=repo to --repo=flatpak-repo to match .gitignore - Add architecture note for protoc x86_64 binary Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add Flatpak install instructions to README Add a dedicated section for installing via Flatpak on Linux, clarify that prerequisites are only needed for building from source, and rename "Installation" to "Build from Source" for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: match StartupWMClass to Flatpak app_id Use reverse-DNS format org.dash.DashEvoTool to match the Wayland app_id set via ViewportBuilder::with_app_id(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use ** glob for branch trigger to match feat/flatpak Single * doesn't match path separators in GitHub Actions branch filters. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add aarch64 Flatpak build and caching to CI - Add matrix strategy for parallel x86_64 and aarch64 builds - Patch protoc URL/sha256 per architecture at build time - Cache .flatpak-builder directory keyed on arch + manifest + lockfile - Pin actions/cache to SHA Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: convert desktop and metainfo files to LF line endings Flatpak builder validates desktop files and rejects CRLF line endings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: cancel in-progress Flatpak builds on new push Add concurrency group keyed on git ref so a new push cancels any running build for the same branch or release. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review findings for Flatpak packaging - Remove unnecessary --filesystem=xdg-config/dash-evo-tool:create (Flatpak already redirects XDG_CONFIG_HOME to sandbox) - Add categories and keywords to AppStream metainfo for discoverability - Update README with both x86_64/aarch64 install commands, uninstall instructions, and Flatpak data path note - Clarify aarch64 comment in manifest to reference CI sed patching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: workflow timeout and perms * fix: move permissions to job level in Flatpak workflow Step-level permissions are not valid in GitHub Actions. Move contents: write to the job level where it is needed for release attachment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * build: cache Cargo registry and target in Flatpak CI Bind-mount host-side cargo-cache and cargo-target directories into the Flatpak build sandbox so CARGO_HOME and target/ persist across builds. Uses split restore/save with cleanup of incremental and registry/src (similar to Swatinem/rust-cache). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: scope cargo cache bind-mount sed to build-args only The previous sed matched --share=network in both finish-args and build-args, corrupting finish-args. Use a sed range to only target the build-args section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Apply suggestions from code review --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
75c8e63 to
95a1809
Compare
Aligns elapsed display with the countdown timer which already adds 1 to avoid showing "0s" immediately. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
95a1809 to
e0f0ce9
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…sage-display-applied
Resolves conflict in mint_tokens_screen.rs by combining both Warning handling from the base branch and refresh_banner clearing from HEAD. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge v1.0-dev (d45b3ee) into the unified message display branch, resolving 45 conflicted files across the codebase. Resolution strategy: - Keep the unified MessageBanner approach (design branch) for all screen-level message display, replacing v1.0-dev's inline colored labels and per-screen message fields - Keep simplified status enums without timestamp parameters, since MessageBanner handles elapsed-time display via BannerHandle - Merge v1.0-dev's MessageBanner enhancements: details/suggestion fields, collapsible details section, tracing-based logging, BannerState::new()/reset_to() constructors, distinct error icon - Merge v1.0-dev's batch refresh counting in IdentitiesScreen (pending_refresh_count, total_refresh_count, pluralized messages) - Merge v1.0-dev's ContactDetailsScreen database loading and Platform persistence via backend tasks - Merge v1.0-dev's improved UX text in ContactRequests (informative label instead of non-functional Cancel button) - Fix post-merge issues: remove references to deleted `message` field in ContactDetailsScreen, fix infinite recursion in display_message https://claude.ai/code/session_015EEVFee5cXpgaGASfZcAN3
🔍 Audit Report — PR #604Reviewed by: Claude Code with a 5-agent team:
Skills used: Overall AssessmentThe However, the PR contains 1 critical regression (app crash), 3 high-severity regressions, and several medium-severity logic issues. See inline comments for items within the diff; additional findings below. Findings Summary
¹ Pre-existing or outside the GitHub diff — details below. Pre-existing / Outside-Diff Findings#8 —
#9 — When #10 —
#11 —
#12 — Every Positive Observations
Pre-existing Issue (not this PR)12× 🤖 Reviewed by Claudius the Magnificent AI Agent |
Summary
This is a follow-up PR implementing #601
MessageBannercomponent with global banner API (set_global,replace_global,clear_global_message) andBannerHandlefor lifecycle managementMessageType::Warningvariant and themed banner rendering (info/success/warning/error) with auto-dismiss, elapsed timer, and countdown supportisland_central_panel()—AppState::update()sets them automatically for backend task resultsAppAction::BackendTaskand optionally set aBannerHandle::with_elapsed()for long-running operationsChanges across the codebase
src/ui/components/message_banner.rs—MessageBanner,BannerHandle,BannerStatusapp.rsroutesTaskResultsuccess/error to global banners automaticallyWaitingForResult(timestamp)) toBannerHandle::with_elapsed()MessageBannerAPIDashColorswith banner-specific color constantsMessageBannerSpecial cases handled
send_screen.rs: Kept full-screen spinner layout, removed only inline elapsed labeladd_existing_identity_screen.rs: Useshandle.set_message()for progress text updatesdpns_contested_names_screen.rs: Two separate banner handles (refresh + vote)tokens_screen/mod.rs: Sharedoperation_banneracross sub-screenstransition_visualizer_screen.rs: Only migratedSubmitting, keptInstant-based fade effectsTest plan
cargo clippy --all-features --all-targets -- -D warningspassescargo +nightly fmt --allappliedcargo test --test kittest --all-featurespasses🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests