Skip to content

Comments

feat: unified message banner for all screens#604

Draft
lklimek wants to merge 21 commits intov1.0-devfrom
design/unified-message-display-applied
Draft

feat: unified message banner for all screens#604
lklimek wants to merge 21 commits intov1.0-devfrom
design/unified-message-display-applied

Conversation

@lklimek
Copy link
Contributor

@lklimek lklimek commented Feb 18, 2026

Summary

This is a follow-up PR implementing #601

  • Introduces MessageBanner component with global banner API (set_global, replace_global, clear_global_message) and BannerHandle for lifecycle management
  • Adds MessageType::Warning variant and themed banner rendering (info/success/warning/error) with auto-dismiss, elapsed timer, and countdown support
  • Migrates all ~50 screens from inline status messages and "Refreshing... Time so far: N" indicators to the unified banner system
  • Global banners rendered centrally by island_central_panel()AppState::update() sets them automatically for backend task results
  • Screens only need to return AppAction::BackendTask and optionally set a BannerHandle::with_elapsed() for long-running operations

Changes across the codebase

  • New component: src/ui/components/message_banner.rsMessageBanner, BannerHandle, BannerStatus
  • App integration: app.rs routes TaskResult success/error to global banners automatically
  • 26 screens migrated from inline elapsed-time indicators (WaitingForResult(timestamp)) to BannerHandle::with_elapsed()
  • ~50 screens migrated from inline error/success labels to global MessageBanner API
  • Theme: Extended DashColors with banner-specific color constants
  • Tests: Added kittest UI integration tests for MessageBanner

Special cases handled

  • send_screen.rs: Kept full-screen spinner layout, removed only inline elapsed label
  • add_existing_identity_screen.rs: Uses handle.set_message() for progress text updates
  • dpns_contested_names_screen.rs: Two separate banner handles (refresh + vote)
  • tokens_screen/mod.rs: Shared operation_banner across sub-screens
  • transition_visualizer_screen.rs: Only migrated Submitting, kept Instant-based fade effects

Test plan

  • cargo clippy --all-features --all-targets -- -D warnings passes
  • cargo +nightly fmt --all applied
  • cargo test --test kittest --all-features passes
  • Visual: no more inline "Time so far" / elapsed labels — all elapsed indicators appear in the global banner area
  • Verify banners auto-dismiss for success messages, persist for errors
  • Verify elapsed timer ticks during long-running operations (identity refresh, token operations, etc.)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a unified message banner system for displaying notifications across the application
    • Introduced support for multiple message severity levels including a new Warning type
  • Documentation

    • Added comprehensive documentation for the new message banner architecture, design patterns, and UX specifications
  • Tests

    • Added comprehensive test coverage for the message banner component and its functionality

lklimek and others added 6 commits February 17, 2026 16:32
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>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A 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

Cohort / File(s) Summary
Core MessageBanner Component & Integration
src/ui/components/message_banner.rs, src/ui/components/mod.rs, src/ui/components/styled.rs
Introduces comprehensive MessageBanner component with BannerStatus, MessageBannerResponse, BannerHandle, and global/per-instance APIs. Implements Component trait with rendering, auto-dismiss, elapsed-time display, and deduplication. island_central_panel now renders global banners before screen content.
AppContext & Context Management
src/context/mod.rs, src/app.rs
Adds egui::Context field to AppContext and public accessor method. Propagates ctx parameter through all AppContext::new calls. Integrates MessageBanner feedback in AppState for BackendTaskResult errors, success messages, and theme updates.
Documentation & Specifications
doc/CLAUDE.md, doc/COMPONENT_DESIGN_PATTERN.md, docs/ai-design/2026-02-17-unified-messages/*
Adds documentation for display-only components, MessageBanner architecture, UX specification, lifecycle management, behavior semantics, and interactive HTML mockup with theme switching and countdown timers.
MessageType Enum & Theme Colors
src/ui/mod.rs, src/ui/theme.rs
Adds Warning variant to MessageType enum. Adds DashColors helpers: info_color(), message_color(), and message_background_color() for banner theming across all message types.
Identity Screens Refactoring
src/ui/identities/identities_screen.rs, src/ui/identities/add_existing_identity_screen.rs, src/ui/identities/add_new_identity_screen/mod.rs, src/ui/identities/add_new_identity_screen/*.rs, src/ui/identities/keys/add_key_screen.rs, src/ui/identities/keys/key_info_screen.rs, src/ui/identities/register_dpns_name_screen.rs, src/ui/identities/transfer_screen.rs, src/ui/identities/withdraw_screen.rs, src/ui/identities/top_up_identity_screen/*
Removes per-screen message, error_message, and timestamped WaitingForResult fields. Replaces with refresh_banner (BannerHandle) for managing global banners. Simplifies status enums (WaitingForResult without timestamp, Error instead of ErrorMessage). Updates display_message to use MessageBanner::set_global for errors and info.
Token & Contract Screens Refactoring
src/ui/tokens/burn_tokens_screen.rs, src/ui/tokens/claim_tokens_screen.rs, src/ui/tokens/destroy_frozen_funds_screen.rs, src/ui/tokens/freeze_tokens_screen.rs, src/ui/tokens/mint_tokens_screen.rs, src/ui/tokens/pause_tokens_screen.rs, src/ui/tokens/resume_tokens_screen.rs, src/ui/tokens/set_token_price_screen.rs, src/ui/tokens/transfer_tokens_screen.rs, src/ui/tokens/unfreeze_tokens_screen.rs, src/ui/tokens/add_token_by_id_screen.rs, src/ui/tokens/direct_token_purchase_screen.rs, src/ui/tokens/view_token_claims_screen.rs
Removes per-screen error_message and timestamped WaitingForResult/ErrorMessage state. Adds refresh_banner for managing elapsed-time and error banners. Replaces inline error UI and time-tracking with MessageBanner::set_global calls for global feedback.
Token Search & Creator Screens
src/ui/tokens/tokens_screen/mod.rs, src/ui/tokens/tokens_screen/keyword_search.rs, src/ui/tokens/tokens_screen/my_tokens.rs, src/ui/tokens/tokens_screen/token_creator.rs, src/ui/tokens/tokens_screen/contract_details.rs
Removes backend_message and timestamped status fields; introduces operation_banner for global banner lifecycle. Simplifies ContractSearchStatus and TokenCreatorStatus enums. Replaces backend message rendering with MessageBanner::set_global for errors, info, and progress indicators.
DashPay Contact Screens Refactoring
src/ui/dashpay/add_contact_screen.rs, src/ui/dashpay/contact_details.rs, src/ui/dashpay/contact_info_editor.rs, src/ui/dashpay/contact_profile_viewer.rs, src/ui/dashpay/contact_requests.rs, src/ui/dashpay/contacts_list.rs, src/ui/dashpay/profile_screen.rs, src/ui/dashpay/profile_search.rs, src/ui/dashpay/qr_code_generator.rs, src/ui/dashpay/qr_scanner.rs, src/ui/dashpay/send_payment.rs
Removes per-screen message field and related state management. Updates display_message to no-op with parameters prefixed with underscores. Routes all error and info messaging through MessageBanner::set_global instead of local rendering.
Contract & DPNS Screens Refactoring
src/ui/contracts_documents/add_contracts_screen.rs, src/ui/contracts_documents/contracts_documents_screen.rs, src/ui/contracts_documents/document_action_screen.rs, src/ui/contracts_documents/group_actions_screen.rs, src/ui/contracts_documents/register_contract_screen.rs, src/ui/contracts_documents/update_contract_screen.rs, src/ui/dpns/dpns_contested_names_screen.rs
Removes per-screen error_message and timestamped status fields. Introduces banner handles (refresh_banner, vote_banner, query_banner) for global banner lifecycle. Simplifies enums by removing string payloads and timestamps. Routes messaging through MessageBanner::set_global.
Wallet Screens Refactoring
src/ui/wallets/asset_lock_detail_screen.rs, src/ui/wallets/create_asset_lock_screen.rs, src/ui/wallets/send_screen.rs, src/ui/wallets/single_key_send_screen.rs, src/ui/wallets/wallets_screen/mod.rs, src/ui/wallets/wallet_unlock.rs, src/ui/wallets/wallets_screen/dialogs.rs
Removes message and error_message fields from wallet screens. Adds send_banner (BannerHandle) for managing send progress. Simplifies SendStatus enum variants. Routes errors and confirmations through MessageBanner::set_global. Removes per-instance error rendering and auto-expiration logic.
Tool Screens Refactoring
src/ui/tools/address_balance_screen.rs, src/ui/tools/contract_visualizer_screen.rs, src/ui/tools/document_visualizer_screen.rs, src/ui/tools/grovestark_screen.rs, src/ui/tools/masternode_list_diff_screen.rs, src/ui/tools/platform_info_screen.rs, src/ui/tools/transition_visualizer_screen.rs
Removes error_message and gen/verify_error_message fields. Updates display_message to no-ops or removes per-instance message handling. Introduces submit_banner (BannerHandle) and removes time-based status tracking. Routes errors through MessageBanner::set_global; masternode_list_diff_screen adds Warning color support.
MessageBanner Tests
tests/kittest/main.rs, tests/kittest/message_banner.rs
Adds comprehensive test module with 494 lines covering global/per-instance banner lifecycle, rendering, auto-dismiss, elapsed-time display, deduplication, eviction on max-banner cap, and interaction assertions via egui_kittest harness.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 Banners now dance in the global light,
No more messages scattered left and right!
From screens to center, they hop and play,
Auto-dismissing at end of day!
One banner state to rule them all—
A cozy warren, big and small! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: unified message banner for all screens' clearly and concisely describes the main change: introducing a unified message banner system across the application's screens.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch design/unified-message-display-applied

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lklimek lklimek changed the base branch from v1.0-dev to design/unified-message-display February 18, 2026 20:35
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Implement missing ScreenLike lifecycle methods.

This impl lacks refresh/refresh_on_arrival and change_context, which are required for screens in src/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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Implement missing ScreenLike lifecycle methods.

This impl lacks refresh/refresh_on_arrival and change_context, which are required for screens in src/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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Implement missing ScreenLike lifecycle methods.

This impl lacks refresh/refresh_on_arrival and change_context, which are required for screens in src/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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Surface wallet-selection errors during initialization.

get_selected_wallet can populate error_message, but it’s currently ignored in new(). 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 | 🟡 Minor

Add missing change_context implementation.

This screen already implements refresh and refresh_on_arrival, but change_context is still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines: src/ui/**/*.rs: All screens must implement the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Add missing change_context implementation.

This screen already implements refresh and refresh_on_arrival, but change_context is still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines: src/ui/**/*.rs: All screens must implement the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Add missing change_context implementation.

This screen implements refresh, but change_context is still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines: src/ui/**/*.rs: All screens must implement the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟠 Major

Add 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 the Screen impl 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 | 🟡 Minor

Add 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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Add 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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Add 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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Add 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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Add 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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟠 Major

BroadcastError blocks retry because the parsed contract is discarded.

After an error, BroadcastStatus::BroadcastError is set and the ValidContract payload 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 | 🟡 Minor

Duplicate unreachable Complete check.

Lines 402-405 already check AddKeyStatus::Complete and 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 | 🟡 Minor

Add missing change_context implementation.

This screen implements refresh, but change_context is still missing. Please add it per the ScreenLike trait contract.
As per coding guidelines: src/ui/**/*.rs: All screens must implement the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Implement missing ScreenLike lifecycle methods.

This impl lacks refresh/refresh_on_arrival and change_context, which are required for screens in src/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 the ScreenLike trait with methods: ui(), display_task_result(), display_message(), refresh()/refresh_on_arrival(), and change_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 | 🟡 Minor

Clear stale parsed QR data when input is empty.

If the user clears the input and clicks “Parse QR Code,” parsed_qr_data remains 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 | 🟡 Minor

Add 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.md around
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 of and 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.md around 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 use rust for the render_banner signature (render_banner/ui/process_banner references), change the ASCII art and flow blocks to text (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 -->

thepastaclaw and others added 4 commits February 18, 2026 21:39
* 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>
lklimek and others added 4 commits February 18, 2026 21:43
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>
@lklimek lklimek force-pushed the design/unified-message-display-applied branch from 95a1809 to e0f0ce9 Compare February 18, 2026 20:44
@lklimek lklimek marked this pull request as draft February 18, 2026 20:53
lklimek and others added 5 commits February 18, 2026 22:05
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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>
Base automatically changed from design/unified-message-display to v1.0-dev February 23, 2026 11:31
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
@lklimek lklimek removed the postponed label Feb 24, 2026
@lklimek
Copy link
Contributor Author

lklimek commented Feb 24, 2026

🔍 Audit Report — PR #604

Reviewed by: Claude Code with a 5-agent team:

  • core-reviewer (code-reviewer, Opus) — MessageBanner API design & app integration
  • wallets-reviewer (code-reviewer) — wallet & tools screen migrations
  • tokens-reviewer (code-reviewer) — token screen migrations
  • identity-reviewer (code-reviewer) — identity, DashPay, DPNS & contracts migrations
  • security-engineer (security-engineer) — injection, DoS, memory, CVE research

Skills used: personality, severity, rust-best-practices, security-best-practices


Overall Assessment

The MessageBanner component is well-designed — clean API, 25 kittest tests, bounded memory (MAX_BANNERS=5), proper thread safety. The migration of ~50 screens is broadly correct and consistent, removing ~1000 lines of inline message/timer boilerplate.

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

# Sev File Description Location
1 CRIT update_token_config.rs unimplemented!() replaces ui.label() — crashes app on MarketplaceTradeMode inline
2 HIGH send_payment.rs load_contact_info() replaced with hardcoded "alice.dash" mock inline
3 HIGH contacts_list.rs 4× database errors silently swallowed with let _ = inline
4 HIGH tokens_screen/mod.rs "Token added successfully!" removed, no set_global() replacement inline
5 MED send_screen.rs SendStatus::Error renders bare "Dismiss" button without context inline
6 MED send_screen.rs Sync validation errors hide the form via SendStatus::Error inline
7 MED transition_visualizer.rs Error silently resets to NotStarted, re-enabling submit inline
8 MED masternode_list_diff.rs Hand-rolled render_message_banner() survives migration ¹ below
9 MED message_banner.rs Handle aliasing via dedup — reset_to() wipes first caller's details ¹ below
10 MED message_banner.rs replace_global("") returns dangling BannerHandle ¹ below
11 MED app.rs Raw SDK error strings displayed verbatim (info disclosure, A09) ¹ below
12 MED app.rs Double-display: TaskResult::Error fires both set_global AND display_message ¹ below

¹ Pre-existing or outside the GitHub diff — details below.


Pre-existing / Outside-Diff Findings

#8masternode_list_diff_screen.rs:1561 — Surviving inline banner duplicate

render_message_banner() is a 30-line hand-rolled Frame/RichText/Margin/Stroke inline banner that duplicates the new MessageBanner styling. The screen maintains ui_state.message: Option<(String, MessageType)> and its display_task_result sets it directly instead of routing through MessageBanner::set_global(). Should be migrated to the unified system.

#9message_banner.rs:286-308 — Shared handle aliasing

When set_global is called twice with the same text, reset_to() on the existing banner wipes any details/suggestion set by the first caller. Both callers hold handles to the same banner. Document this aliasing behavior or have reset_to() preserve existing metadata.

#10message_banner.rs:320-326 — Dangling BannerHandle

replace_global("") returns a BannerHandle whose key was never inserted. The handle is dead on arrival but the type system doesn't communicate this. Consider returning Option<BannerHandle>.

#11app.rs:958-961 — Raw error strings (A09)

TaskResult::Error(message) displays e.to_string() from SDK/platform errors verbatim. These can include internal paths, SQL schema, endpoint URLs. A malicious Platform node could inject misleading messages. Consider a thin error-mapping layer for user-facing display.

#12app.rs:917-961 — Double-display pattern

Every TaskResult::Error calls both MessageBanner::set_global AND display_message with the same text. Some screens' display_message implementations also set local banners, causing duplicate feedback. The CLAUDE.md says "Screens only override display_message() for side-effects" — audit screens to ensure compliance.


Positive Observations

  • BannerHandle abstraction is genuinely good — builder-style post-hoc mutation without holding internal state references
  • Text-based deduplication prevents banner spam without requiring caller tracking
  • MAX_BANNERS=5 + auto-dismiss + DETAILS_MAX_HEIGHT scroll cap = solid DoS defence
  • egui RichText provides inherent injection immunity (GPU rasterisation, not HTML)
  • 25 kittest tests — comprehensive for a UI component
  • Dead helpers cleanly removed: dismiss_message, check_message_expiration, set_message, format_elapsed_time
  • ScreenWithWalletUnlock trait cleanup eliminates a leaky abstraction
  • No relevant CVEs in egui 0.33.3, eframe 0.33.3, rusqlite 0.38.0

Pre-existing Issue (not this PR)

12× .expect("No key selected") calls across token screens replace earlier match guards with graceful error display. These are latent panics reachable if the confirmation dialog doesn't gate on selected_key.is_some(). Suggest a follow-up ticket.


🤖 Reviewed by Claudius the Magnificent AI Agent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants