-
Notifications
You must be signed in to change notification settings - Fork 5
[FEATURE] Send Collectibles #489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ansaction settings store
… adjusts review sheet for selected collectible
…rfaces, adds transfer collectible processor for history items
…collectible details when transfer has collectible details
…border on chevron
…ic navigation based on selection in send flow
); | ||
}); | ||
|
||
it("should handle simulation without optional fee", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test is actually wrong and not needed, the transaction is built before submitting.
"/submit-transaction", | ||
mockSubmitParams, | ||
); | ||
expect(mockPost).toHaveBeenCalledWith("/submit-tx", mockSubmitParams); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the route names were incorrect from the previous functional PR.
// we can guess that the contract is either a token or a collectible | ||
// by the type of the 3rd argument. | ||
// Token transfer - (from: Address, to: Address, amount: i128) | ||
// Collectible transfer - (from: Address, to: Address, tokenId: u32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice one
from: sorobanAttributes.from, | ||
to: sorobanAttributes.to, | ||
tokenId: sorobanAttributes.tokenId, | ||
collectibleName: transferedCollectible.name || "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought transferedCollectible.name
would be always present here, is lint complaining?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's because the Collectible type defines this and a few of the other metadata fields as string | undefined
which I think is correct since these fields are not guaranteed to be set(probably in practice will often be set for real ones).
I think it makes sense also use the same fallback here, Unknown Collectible
. wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is done in ad64b10
…ollectible review
src/components/screens/SendScreen/screens/TransactionAmountScreen.tsx
Outdated
Show resolved
Hide resolved
src/components/screens/SendScreen/screens/TransactionProcessingScreen.tsx
Outdated
Show resolved
Hide resolved
src/components/screens/SendScreen/screens/TransactionProcessingScreen.tsx
Outdated
Show resolved
Hide resolved
{type === "token" && selectedBalance && ( | ||
<TokenIcon token={selectedBalance} size="lg" /> | ||
)} | ||
{type === "collectible" && selectedCollectible && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please review all places that could make use of the new enum types this PR is adding? thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are all added in 4361451
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-10-20.at.10.40.29.mp4@CassioMG a couple of notes here - |
…een.tsx Co-authored-by: Cássio Marcos Goulart <[email protected]>
…gScreen.tsx Co-authored-by: Cássio Marcos Goulart <[email protected]>
…gScreen.tsx Co-authored-by: Cássio Marcos Goulart <[email protected]>
@aristidesstaffieri thanks for the video, looks great. I noticed one thing: after you have a collectible selected and you change its destination address the app is stacking new screens (of the same functionality) on top of each other, which suggests the code is "navigating" to a "new" screen instead of "popping" (or "going back") to an existing screen. I think ideally we should try to avoid that as all the screens on the stack would react to changes in state which could lead to weird unexpected behaviors. Wdyt?
That's good to know. Yeah agree this should be a separate task. Would you mind creating it and setting as P1 so we try tackling it soon? Thanks |
added in 2f2f3ec |
…appropriate store state based on nav targets
@CassioMG I've refactored the nav flows to avoid this, check out 514d118 Here's a summary of the nav flow for send-
From CollectibleReview (navigateToSelectContactScreen)// Always replace to avoid duplicating SearchContacts
navigation.replace(SEND_SEARCH_CONTACTS_SCREEN) From TransactionTokenScreen (handleCollectiblePress)saveSelectedCollectibleDetails(collectibleDetails)
if (recipientAddress) {
// Already have recipient, go directly to review
navigation.navigate(COLLECTIBLE_REVIEW)
} else {
// Need to select recipient first
navigation.navigate(SEND_SEARCH_CONTACTS_SCREEN)
} State Management (useTransactionSettingsStore)When to Set State
When to Clear State
State Persistence During Navigation
I don't love how complex this flow is, SearchContact has to be aware of nav history and store state in order to make the right nav choices. Im curious if you see any easier ways to construct this flow or if you have any thoughts on how to simplify the send flow UX? As a side note to this, the send flow navigation is pretty complex at this point but one thing that makes this flow more error prone is the use of remote state in the store for tx details. Every screen in the flow needs to be aware and responsible for setting/clearing the correct data for the next or previous screens to work correctly. This is a smell imo, and creates a view managed data store. We can move this state away from the store and into hooks that are consumed at common parent entry points in order to let the react lifecycle clear any stale data naturally. IMO the prop drilling this creates is a good trade off to avoid the stale data problems that come with complex view flows managing remote state outside of the react lifecycle. |
if ( | ||
searchContactCount > 1 || | ||
currentRouteName !== SEND_PAYMENT_ROUTES.SEND_SEARCH_CONTACTS_SCREEN | ||
) { | ||
// We have duplicate SearchContacts or we're not at the current screen (shouldn't happen) | ||
// Use replace() to swap | ||
navigation.replace( | ||
SEND_PAYMENT_ROUTES.SEND_COLLECTIBLE_REVIEW, | ||
selectedCollectibleDetails, | ||
); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the navigation.replace()
command replaces the current screen with an existing one in stack if any?
Otherwise by reading the code I believe the Flow 4
would look like the following:
Flow 4: CollectibleDetail → Send → Change Contact (Circular Navigation)
[CollectibleDetail]
↓ (navigate)
[SearchContacts]
↓ (select contact, navigate - first time)
[CollectibleReview]
↓ (tap contact to change, replace CollectibleReview with another SearchContacts)
[SearchContacts]
↓ (select new contact, replace SearchContacts with CollectibleReview - circular nav detected)
[CollectibleReview]
↓ (Back button)
[SearchContacts]
↓ (Back button)
[CollectibleDetail]
Stack at each step:
- [CollectibleDetail]
- [CollectibleDetail, SearchContacts]
- [CollectibleDetail, SearchContacts, CollectibleReview] (navigate)
- [CollectibleDetail, SearchContacts, SearchContacts] (edit contact action replaces CollectibleReview with another SearchContacts, we now momentarily have 2 SearchContacts in stack)
- [CollectibleDetail, SearchContacts, CollectibleReview] (selecting contact replaces the duplicate SearchContacts with CollectibleReview since SearchContacts count > 1)
- [CollectibleDetail, SearchContacts] (back)
- [CollectibleDetail] (back)
Which works but we'd momentarily have 2 SearchContacts screens on stack.
Have we tried using popTo()
instead to handle the duplicate screen issues? It appears to have the behavior we want:
So whenever users tap on edit contact we could do something like this: navigation.popTo(SEND_PAYMENT_ROUTES.SEND_SEARCH_CONTACTS_SCREEN)
and hopefully it should work out of the box.
I'm actually surprised (and happy) with the current popTo()
description as formerly I believe it used to just throw an error when the screen you were trying to pop to was not in stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the
navigation.replace()
command replaces the current screen with an existing one in stack if any?
hey yeah so I believe the conditional avoids this. Here is a walk through.
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-10-22.at.09.12.33.mp4
@aristidesstaffieri thanks for all the navigation flows/graphs they are really helpful to understand what the PR is proposing.
I've just posted a suggestion on using
Thanks for raising this. I agree with you that using navigation parameters and/or hooks to handle those flows would be better instead of using a shared data store managed by the views which is less ideal in this case and prone to hard-to-detect bugs. At this point this would probably be a big refactor, but IMO something still worth to take on once we have some bandwidth. I'll create a ticket for it. |
Thanks yeah I agree this is a whole task on it's own, thanks. |
Update: we've discussed the settings refactor with the team in today's Dev Sync meeting and everyone agreed it would be worth the refactor. We'll plan on tackling it in some upcoming Sprint. |
…ck, adds more cross flow store resets
navigation.dispatch( | ||
StackActions.popTo( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to use navigation.dispatch + StackActions here? Could we do something more straightforward like navigation.popTo()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like you can, updated in d9fd966
navigation.replace(SEND_PAYMENT_ROUTES.SEND_SEARCH_CONTACTS_SCREEN); | ||
// Use popTo to navigate back to SearchContacts | ||
// If SearchContacts exists in stack, pops back to it; otherwise adds it | ||
navigation.dispatch( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, I thought we could do it like navigation.popTo() directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated in d9fd966
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! left a suggestion on a cleaner syntax for the popTo() action
Closes #453 #454 #455
What
Adds the ability to send a collectible.
Adds a type prop(token or collectible) to several steps of the send flow(transaction details, send review, transaction processing).
Adds collectible tab in the token selection step, updates nav to follow designed nav structure.
Updates all review screens(send/swap/generic tx) to use updated summary style with icons on the left.
Adds send button in collectible detail view.
Adds logic to distinguish between token transfers and collectible transfers in helpers(getArgsForTokenInvocation).
Adds analytics events and translations for collectible related additions.
Adds new service functions and duck methods to get and store collectibles details in send flow.
Why
Roadmap feature.
Known limitations
N/A
Checklist
PR structure
Testing
Release
History:

Collectible Details:

Send Flow:
