Skip to content

fix(dm): handle NewOrder book republish after pre-Active taker cancel#74

Merged
arkanoider merged 3 commits into
mainfrom
fix/new-order-book-republish-my-trades
May 19, 2026
Merged

fix(dm): handle NewOrder book republish after pre-Active taker cancel#74
arkanoider merged 3 commits into
mainfrom
fix/new-order-book-republish-my-trades

Conversation

@arkanoider

@arkanoider arkanoider commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Trade DM listener: Handle Action::NewOrder with Payload::Order + status: pending on the trade-DM path (not only the create-order waiter). Pre-Active taker takes are deleted and removed from Messages; pre-Active maker listings revert to pending, drop from Messages, and refresh the My Trades sidebar cache.

  • My Trades UX: Event-driven my_trades_maker_book cache (SQLite maker + pending) merged into build_active_order_chat_list so republished listings stay visible without per-frame DB reads. OperationResult::MyTradesMakerBookChanged triggers a silent cache refresh.

  • Refactors: Extract helpers (try_handle_new_order_trade_dm, revert_maker_to_pending_on_book_republish, drop_pre_active_taker_take, projection snapshot helpers) for clarity and DRY.

  • Status transitions: WaitingTakerBond can revert to Pending; FiatSentOk maps to Status::FiatSent in inferred status.
    Motivation

When a taker cancels before Active, Mostro often republishes the order with NewOrder rather than Canceled. The listener previously ignored all trade-DM NewOrder messages, so makers did not revert to book state and My Trades could lose the listing after Messages cleanup.

Summary by CodeRabbit

  • New Features

    • Added a "My Trades" maker-book cache to surface pending maker listings alongside chat-derived orders; background refreshes trigger UI updates without popups.
  • Bug Fixes

    • Improved handling of pre-active maker/taker order republish to avoid stale or regressed order states.
    • Fixed order state sync on republished listings and improved navigation/selection stability in active orders.
  • Documentation

    • Clarified DM listener behavior for NewOrder and error cases.

Review Change Stack

When Mostro republishes a listing via Action::NewOrder (not Canceled), makers
revert to pending and stay visible in My Trades via an event-driven SQLite
cache; takers drop stale take rows from Messages. Extract helpers for clarity
and allow WaitingTakerBond to revert to Pending.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 432e251b-811a-4e9b-8726-bdd68ac9be6a

📥 Commits

Reviewing files that changed from the base of the PR and between e1908a4 and 13bffae.

📒 Files selected for processing (3)
  • docs/DM_LISTENER_FLOW.md
  • src/ui/helpers/order_chat_projection.rs
  • src/util/dm_utils/mod.rs
✅ Files skipped from review due to trivial changes (1)
  • docs/DM_LISTENER_FLOW.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ui/helpers/order_chat_projection.rs

Walkthrough

Adds a maker-book cache to AppState with DB→row projection helpers, populates it at startup, exposes a signaling sender to request cache refresh from background DM logic, updates the main loop to refresh on specific OperationResult values, and refactors trade-DM handling to special-case NewOrder/pre-Active flows.

Changes

Maker-book cache and UI integration

Layer / File(s) Summary
Maker-book state and projection helpers
src/ui/app_state.rs, src/ui/helpers/order_chat_projection.rs
AppState gains my_trades_maker_book: Vec<OrderChatListItem>. New helpers order_chat_list_item_from_db_order, sort_order_chat_rows, active_order_chat_list_len, and active_order_chat_list_snapshot project DB orders and messages into a stable-ordered active chat list; build_active_order_chat_list now accepts maker_book alongside messages.
Startup cache initialization
src/ui/helpers/startup.rs
New refresh_my_trades_maker_book_cache queries user history orders from SQLite and repopulates app.my_trades_maker_book using order_chat_list_item_from_db_order.
UI consumers and navigation
src/ui/key_handler/*, src/ui/tabs/*, src/util/dm_utils/order_ch_mng.rs
Chat helpers, enter handlers, navigation, tab rendering, and message-removal logic now use active_order_chat_list_snapshot/active_order_chat_list_len so maker-pending rows are included in active lists and selection bounds.
UI exports
src/ui/helpers/mod.rs
Re-exports expanded to include the new projection and startup helpers (active_order_chat_list_len, active_order_chat_list_snapshot, order_chat_list_item_from_db_order, refresh_my_trades_maker_book_cache).

OperationResult signaling and main wiring

Layer / File(s) Summary
Operation result variant and render suppression
src/ui/orders.rs, src/ui/operation_result.rs
Adds OperationResult::MyTradesMakerBookChanged, classifies it as a short popup height, and suppresses UI rendering for that variant inside render_operation_result.
Sender module and re-exports
src/util/dm_utils/order_result_tx.rs, src/util/dm_utils/mod.rs, src/util/mod.rs
Adds a mutex-protected Option<UnboundedSender<OperationResult>> with set_order_result_tx to register and try_notify_my_trades_maker_book_changed to notify; re-exports these functions through dm_utils and util.
Main event loop wiring
src/main.rs
Registers the order_result_tx sender at startup (set_order_result_tx) and updates the event loop to detect MyTradesMakerBookChanged and Success(_) results: when refresh is needed and the role is User, it calls refresh_my_trades_maker_book_cache; it skips handle_operation_result only for MyTradesMakerBookChanged.

Trade DM and NewOrder handling

Layer / File(s) Summary
Status mapping and transition rank
src/util/order_utils/helper.rs
inferred_status_from_trade_action maps Action::FiatSentOkStatus::FiatSent. status_phase_rank_for_actor treats WaitingTakerBond as the same rank as Pending; adds a unit test for WaitingTakerBondPending reversion.
NewOrder dispatch, pre-Active helpers, and tests
src/util/dm_utils/mod.rs
Adds DB-derived predicates (order_status_from_row, is_pre_active_taker_take, is_pre_active_maker_listing), drop_pre_active_taker_take, extends upsert_order_from_trade_dm to label persisted NewOrder, implements try_handle_new_order_trade_dm to handle pre-Active taker drop, maker republish→Pending revert, and range-child pending persistence, and replaces the inline "regress" check with new_order_would_regress_messages_row. Adds unit tests for these cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • MostroP2P/mostrix#47: touches related UI operation-result wiring and DM routing changes that interact with sidebar/result handling.
  • MostroP2P/mostrix#54: overlaps on deriving MyTrades active list and chat-selection logic changes.

Suggested reviewers

  • mostronatorcoder
  • grunch

Poem

🐰 In SQLite's quiet burrow I hop,
A maker-book cached at every stop.
NewOrder paths I sort and mend,
UI lists line up end to end.
Hooray — fresh rows for every friend!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(dm): handle NewOrder book republish after pre-Active taker cancel' directly describes the main technical change: handling NewOrder messages in the DM listener after pre-Active taker cancellation, which aligns with the PR's core objective of fixing book republish flows.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/new-order-book-republish-my-trades

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/helpers/startup.rs (1)

121-132: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Always refresh maker-book cache even when startup active-orders query fails.

The early return on Line 130 can bypass Line 152, so maker pending rows are never loaded in that failure path.

Suggested fix
-    let Ok(rows) = Order::get_startup_active_orders(pool).await else {
-        return;
-    };
+    let rows = match Order::get_startup_active_orders(pool).await {
+        Ok(rows) => rows,
+        Err(e) => {
+            log::warn!("Failed to load startup active orders: {}", e);
+            Vec::new()
+        }
+    };

Also applies to: 148-153

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/helpers/startup.rs` around lines 121 - 132, The startup function
load_user_order_chats_at_startup currently returns early if
Order::get_startup_active_orders(pool).await fails, which prevents loading the
maker-book pending rows; remove or change that early return so that failure to
get active orders does not skip the maker-book refresh — keep calling the maker
pending/cache refresh logic (the code that loads maker `pending` rows and
updates the maker-book cache) even when Order::get_startup_active_orders returns
Err, e.g. handle the Result with a match or if let that logs the error but
continues to run the maker-book loading path; ensure
sync_user_order_history_messages_from_db(pool, app).await remains called as-is
and that any references to Order::get_startup_active_orders or its result are
guarded so they don’t prevent the maker pending cache update.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/ui/helpers/order_chat_projection.rs`:
- Around line 179-184: The helper active_order_chat_list_len currently swallows
a poisoned app.messages.lock and returns 0; change it to treat a poisoned lock
as a fatal internal error like other code paths: when app.messages.lock()
returns Err(e) log the error (including e) and abort the process (panic or
std::process::exit(1)) instead of returning 0 so the UI does not continue in an
inconsistent state; apply the same change pattern to the other projection
helpers that lock app.messages (the block around lines 188-193) and keep calls
to build_active_order_chat_list and app.my_trades_maker_book intact.

---

Outside diff comments:
In `@src/ui/helpers/startup.rs`:
- Around line 121-132: The startup function load_user_order_chats_at_startup
currently returns early if Order::get_startup_active_orders(pool).await fails,
which prevents loading the maker-book pending rows; remove or change that early
return so that failure to get active orders does not skip the maker-book refresh
— keep calling the maker pending/cache refresh logic (the code that loads maker
`pending` rows and updates the maker-book cache) even when
Order::get_startup_active_orders returns Err, e.g. handle the Result with a
match or if let that logs the error but continues to run the maker-book loading
path; ensure sync_user_order_history_messages_from_db(pool, app).await remains
called as-is and that any references to Order::get_startup_active_orders or its
result are guarded so they don’t prevent the maker pending cache update.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e891980-8939-4136-8571-fba0af872dcb

📥 Commits

Reviewing files that changed from the base of the PR and between aeee59f and e1908a4.

📒 Files selected for processing (16)
  • src/main.rs
  • src/ui/app_state.rs
  • src/ui/helpers/mod.rs
  • src/ui/helpers/order_chat_projection.rs
  • src/ui/helpers/startup.rs
  • src/ui/key_handler/chat_helpers.rs
  • src/ui/key_handler/enter_handlers.rs
  • src/ui/key_handler/navigation.rs
  • src/ui/operation_result.rs
  • src/ui/orders.rs
  • src/ui/tabs/order_in_progress_tab.rs
  • src/util/dm_utils/mod.rs
  • src/util/dm_utils/order_ch_mng.rs
  • src/util/dm_utils/order_result_tx.rs
  • src/util/mod.rs
  • src/util/order_utils/helper.rs

Comment thread src/ui/helpers/order_chat_projection.rs

@mostronatorcoder mostronatorcoder Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I found a blocker in the new Action::NewOrder handling on the trade-DM path.

Right now handle_trade_dm_for_order() does this:

if matches!(action, Action::NewOrder) {
    let _ = try_handle_new_order_trade_dm(...).await;
    return;
}

But try_handle_new_order_trade_dm() only consumes a very specific subset of NewOrder messages:

  • Payload::Order
  • with status == pending
  • and then only the republish / range-child cases it knows how to handle

For every other NewOrder trade DM, the helper returns false and the caller still returns immediately, so the message is silently dropped.

That means this PR changes behavior from:

  • "ignore NewOrder only by the previous explicit rule"

to:

  • "always short-circuit NewOrder, even when the new helper did not actually handle it"

So if Mostro emits any valid NewOrder trade DM outside this exact pending republish shape, it will now be discarded with no fallback path.

I think the caller must only early-return when try_handle_new_order_trade_dm() returns true, and otherwise continue through the normal trade-DM flow (or keep the previous ignore behavior explicitly limited to the cases you are sure about).

Because the new branch currently broadens silent dropping beyond the intended republish fix, I am marking this as request changes.

@mostronatorcoder

Copy link
Copy Markdown
Contributor

Thanks, I understand the intended protocol case better now.

My concern is not that NewOrder should never be intercepted here. I agree that the republish-to-pending case after a pre-Active taker cancel, and the range-child creation case, are exactly the ones that should be handled specially and removed from the Messages trade flow.

My concern is narrower: the current caller short-circuits all trade-DM NewOrder messages, even when try_handle_new_order_trade_dm() returns false.

So the code currently expresses:

  • every NewOrder on this path must be consumed here

not:

  • only these known pending republish / range-child NewOrder cases must be consumed here

If the protocol guarantees that trade-DM NewOrder can only ever appear in those specific shapes, then I am fine with that direction. But in that case I would like to see that invariant made explicit in the code/tests, because right now the safety relies on an assumption that is not obvious from the implementation itself.

So I am happy to re-evaluate if the guarantee is:

  1. documented clearly in the code/comments, and/or
  2. covered by tests that show other NewOrder trade-DM shapes are impossible / intentionally ignored.

Without that, I still think the current unconditional early return is too broad for a correctness-sensitive DM path.

@arkanoider

Copy link
Copy Markdown
Collaborator Author

Tested and fixed mostronator rant, LGTM!

@arkanoider arkanoider merged commit c476ce4 into main May 19, 2026
11 checks passed
@arkanoider arkanoider deleted the fix/new-order-book-republish-my-trades branch May 19, 2026 13:36
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.

1 participant