Skip to content

Pick offer assets from searchable pickers and keep split settings - #1012

Open
Rigidity wants to merge 10 commits into
rigidity/redesign-offer-asset-rowsfrom
rigidity/offer-asset-pickers
Open

Rigidity wants to merge 10 commits into
rigidity/redesign-offer-asset-rowsfrom
rigidity/offer-asset-pickers

Conversation

@Rigidity

@Rigidity Rigidity commented Nov 15, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #1007 in the create offer stack. Three changes: the pickers used to add assets are replaced, a bug where the offer's split settings were forgotten is fixed, and there's a new setting for splitting coins by default.

Split settings are kept

"One offer per NFT", "Split coins first" and the split fee were local state on the create offer page, so they reset whenever you left the page and came back, unlike the assets. They're now part of OfferState, alongside the assets.

The network fee input also wasn't given its stored value. When you came back to the page, it showed the default fee while the offer still held the fee you had entered. It now shows the stored fee. The fee handlers ignore changes that come from the value prop rather than from typing. Clearing the offer (Cancel, or Done after creating it) re-renders the page with an empty fee before it navigates away. Without this, the input reported that as a new value and recreated the offer, so the Offers page sent you straight back.

NftGroupCard used to pass splitNftOffers through router state. It now sets it in the offer state, and the unused setSplitNftOffers prop is removed from NftGroupCard and NftCardList.

Split coins by default

A new Split Coins for Offers setting in Settings → Transactions, next to Default Offer Expiry, sets whether new offers start with "Split coins first" on. It defaults to on, and like the other offer defaults it's stored in local storage (default-offer-split). It can still be changed for each offer.

New offers also start with the default fee as the split fee. Before, the split fee box filled in the default fee when it first appeared. Now the offer state starts with it, so the split fee is already set before that box has rendered.

Asset pickers

Before, adding an asset created an empty row with a half-width dropdown. Its popover matched the trigger's width, so names and asset IDs were cut off. NFTs came 8 per page with arrow buttons.

  • + Token opens a picker straight away (TokenPicker). A row is only created once a token is picked, and its amount input takes focus.
    • When offering, the list shows each token's balance and leaves out tokens with none.
    • When requesting, it lists every known CAT and accepts a pasted asset ID. On mainnet and testnet11, that's every CAT on Dexie (about 950 now), which the backend's CAT queue loads into the database. So the list shows 50 at a time with "Show more", starting with XCH and then the tokens you hold.
    • Search ranks an exact ticker match first, then names or tickers that start with the search, then ones that contain it, then asset ID prefixes.
    • Tokens already in the offer are disabled.
  • The token in a row is a compact chip (icon, ticker) at the start of the row. Clicking it reopens the picker to change the token.
  • Max is an icon button inside the amount field.
  • + NFT and + Option on the offered side open multi-select pickers (NftPicker, OptionPicker), both built on a shared MultiAssetPicker. Each has search (by name or a pasted ID) and "Show more". The picker stays open so several can be added at once, and picking one again removes it. Options are searched with getOptions' find_value, which only returns options the wallet still holds. Expired options are left out.
  • Offered NFTs and options show as read-only rows. NFT rows show the thumbnail, name and short ID. Option rows show the name and terms, e.g. "10 SBX for 1 TXCH, expires Dec 14, 2026".
  • Requested NFTs keep the ID input and lookup preview from Redesign the asset rows on the create offer page #1007. Requested options get an ID input that flags an invalid ID. Both inputs take focus when added.
  • NftSelector and OptionSelector are removed, since nothing uses them any more. TokenSelector stays because MintOption uses it.

Testing

  • pnpm prettier:check, pnpm lint, tsc --noEmit and pnpm test:unit (220 tests, after rebasing onto main) pass.
  • New tests:
    • The split choices and split fee survive leaving the page and coming back.
    • Cancel clears the offer even when the fee input echoes the reset value.
    • New offers split coins by default with the default fee, and don't when the setting is off.
    • Picking a token adds exactly one row.
    • A token can't be offered twice.
    • Requesting by a pasted asset ID.
    • A long list of requestable tokens shows 50 at a time, with XCH and held tokens first.
    • An exact ticker match comes before a partial one.
    • Toggling NFTs in the picker.
    • The option picker lists only unexpired options and adds the picked one.
  • Checked in the sandbox at desktop and phone widths:
    • Added tokens and several NFTs through the pickers.
    • Left the page for Wallet and NFTs and came back. The split choices, split fee and network fee were all kept.
    • Created an offer with a coin split, which showed the expected summary and "Offer Created".
    • Cancel, and Done after creating an offer, both land on the Offers page.
    • With a default fee of 0.0003, a new offer started with splitting on and a split fee of 0.0003. With the setting turned off, a new offer started with splitting off.
    • Minted two options, added both through the picker, filled an amount with the Max icon, and created an offer with them.

Review

Three review rounds, each run with separate correctness, tests and simplification reviewers, plus a final correctness check.

  • Round 1 found that clicking the collection "added to offer" toast after the offer had been cleared crashed New Offer: zustand merged the partial update into a null store. splitOfferNfts() in src/state.ts now only updates an offer that still exists.
  • Round 2 found that Cancel still recreated the offer if you'd come back to it with a stored fee. FeeAmountInput's one-time default fill hadn't run at mount, so it ran when Cancel cleared the fee. The input now fills in the default fee only when it first appears empty. This is a shared component with 17 callers. The other callers mount empty (WalletConnect mounts with a suggested fee), so their first fill is unchanged. Clearing a field later no longer snaps back to the default.
  • Round 3 found nothing at major or above.

Smaller fixes along the way:

  • Trim pasted option IDs.
  • Only accept options the wallet still holds.
  • Use getNfts' total for "Show more".
  • Ignore stale NFT loads when fetching thumbnails.
  • Share NftSummary between the picker and the offered NFT row.
  • Give TokenIcon a size prop.
  • Tests for Cancel and Done, the stored fee coming back, changing a row's token, requested option IDs, and the pickers' search and paging.

Decisions to confirm

  • Requested options are only checked for a valid ID format, with no lookup or preview like requested NFTs have.
  • Pasting the ID of an NFT you no longer hold still lists it in the NFT picker, as the old selector did. NftRecord has no owned flag, so fixing it needs a backend change.

New strings are wrapped with Lingui; the next release needs pnpm extract.

@Rigidity
Rigidity added this pull request to stack #1008 November 15, 2026 03:02
@Rigidity Rigidity changed the title rigidity/offer asset pickers Pick offer assets from searchable pickers and keep split settings Nov 15, 2026
@Rigidity
Rigidity marked this pull request as ready for review November 15, 2026 03:02
@Rigidity
Rigidity force-pushed the rigidity/offer-asset-pickers branch from 51dabd6 to 6f16ff4 Compare November 15, 2026 04:29
Adding a token or NFT now opens a picker straight away, so empty rows
are never left behind. The token picker lists balances and accepts a
pasted asset ID when requesting; the NFT picker stays open so several
NFTs can be added at once. The token in a row is a compact chip that
reopens the picker, and offered NFTs show as read-only summaries.
The token chip now comes first in a token row, and Max is an icon
inside the amount field. Offered options are added from a multi-select
picker and shown as read-only rows with their terms, sharing the picker
shell with NFTs; requested option IDs are checked as they're typed.
Clearing the offer re-renders the page with an empty fee before it
navigates away, and the number input reports that prop change as a new
value. Writing it back recreated the offer, so the offers page sent you
straight back. Prop-driven changes only echo the stored fee, so ignore
them.
New offers start with "Split coins first" on, unless it's turned off in
Settings > Transactions, and with the default fee as the split fee.
Requested tokens come from every CAT on Dexie, nearly a thousand, which
were all drawn at once. Show 50 at a time, with XCH and the tokens you
hold first, and put exact ticker matches ahead of partial ones.
- Only turn on one offer per NFT from the collection toast while an
  offer is still being made; after it was cleared, a partial state
  crashed the New Offer page
- Store a pasted option ID without surrounding whitespace
- Share NftSummary between the NFT picker and the offered NFT row
- Use getNfts' total to tell whether there are more NFTs
- Test the split fee on Cancel, the network fee coming back, changing a
  row's token, and checking requested option IDs
- Fill in the default fee only when the fee input first appears empty.
  Coming back to an offer with a stored fee and pressing Cancel let the
  later fill recreate the offer
- Only accept a pasted option ID the wallet still holds
- Give TokenIcon a size instead of overriding its image's classes
- Test Done after creating an offer
getOptions already searches names and tickers and finds a pasted option
ID among the options the wallet holds, so use it instead of filtering a
list of up to 1000 on the client. Test that the NFT and option pickers
search by what was typed and that Show more loads more NFTs.
A search typed while the first list was loading could finish first, and
the older list then replaced the IDs whose thumbnails were loaded.
@Rigidity
Rigidity force-pushed the rigidity/offer-asset-pickers branch from 5f98e74 to 4692418 Compare November 15, 2026 05:12

This branch has not been deployed

No deployments
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