-
Notifications
You must be signed in to change notification settings - Fork 9
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
[ABW-3439] Card Carousel #1187
[ABW-3439] Card Carousel #1187
Conversation
|
||
// MARK: - CarouselCard | ||
public enum CarouselCard: Hashable, Sendable { | ||
case threeSixtyDegrees |
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 plan on letting the Cards be an enum? Is it because we need to know what to do when the card is pressed, and we dont wanna represent that with a closure? Especially not if this is to be moved into Sargon?
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 the repertoire of cards is very limited, and they are completely static, so it seems like a good model. The action will be known at compile time, not sure it makes sense to store it on the model, it can just be handled in the reducer, or possibly the client.
4665695
to
b2d6ebb
Compare
52334ba
to
2a6e024
Compare
RadixWallet/Clients/CardCarouselClient/CardCarouselClient+Interface.swift
Outdated
Show resolved
Hide resolved
...s/Resources/Assets.xcassets/CardCarousel/carousel_icon_continueOnDapp.imageset/Contents.json
Outdated
Show resolved
Hide resolved
...s/Resources/Assets.xcassets/CardCarousel/carousel_full_background_360.imageset/Contents.json
Outdated
Show resolved
Hide resolved
RadixWallet/Clients/CardCarouselClient/CardCarouselClient+Live.swift
Outdated
Show resolved
Hide resolved
|
||
@CasePathable | ||
public enum ViewAction: Equatable, Sendable { | ||
case didAppear |
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.
usually this is a task
|
||
return .init( | ||
handleEvent: { event in | ||
switch event { |
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.
Hmm, not 100% sure about this approach. Should AppEventsClient
perform some actions on the events, or should it just propagate the events?
In the later case the, HomeCardsClients would listen for the events emitted by the AppEventsClient and perform the actions on its own.
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.
I liked this idea a lot, so gave it a try here. However, there seems to be a race condition error since the .appStarted
event is never received by the listener. I even added this handy method to be called by the AppEventsClient
on its initialization, to make sure the HomeCardsClient
subscribes to events before the first one is dispatched, but problem was still present.
We can work on some delays but even having the AppsEventsClient
starting the HomeCardsClient
(and whatever other listener client we would have in the future) goes against the whole reasoning behind this change.
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.
resolved with AsyncReplaySubject
if deepLink.isDeferred, let value = deepLink.clickEvent["deep_link_value"] as? String { | ||
appEventsClient.handleEvent(.deepLinkReceived(value)) |
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.
We already have DeepLinkHandlerClient, could we extend it to handle the deferred deepLink?
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.
mm I am not sure I prefer it this way, since DeepLinkHandlerClient
is about non deferred deep links. The logic for deferred vs non deferred is totally different, so not sure if it wouldn't add more confusion to put it there just for the sake of being a deep link.
public var walletStarted: WalletStarted | ||
public var walletCreated: WalletCreated | ||
public var deepLinkReceived: DeepLinkReceived |
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 should express some command, like handle...
, or on...
. similar to the remove
above.
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.
ignored as per changes to AppEventClient
@MainActor | ||
private var coreView: some SwiftUI.View { | ||
TabView(selection: $selectedCardIndex) { | ||
ForEach(Array(store.cards.enumerated()), id: \.1) { index, card in |
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 we use \.element
instead of \.1
?
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.
What is the purpose of enumerating it and then use element
as id
, instead of just doing \id:.self
? And stepping back, what was wrong with having the card be identifiable
?
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.
We use the index to tag each card, which is necessary for the positionIndicator defined below. We can extend the card model (now defined in Sargon), and make it Identifiable
by defining an id for each. Same result, just need some extra code.
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 problem with that is that swiftui will not be able to handle insertion and removal animations properly if the the index is the id. For example if you have four cards and remove the third card, with id and index 2, the collection will still have a card with id 2, but it will be the one that used to have id 3. In short, using index as id with a dynamic collection is not supported in SwiftUI. That is also why I called my helper type that wraps up the functionality you use here, "ForEachStatic".
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.
We aren't using the index
as the id
, we are using the \.element
to identify them.
We are using the index
to tag them, and that works correctly on your example since after removing card with index: 2
, the index (and tag) is properly updated with the new set of elements.
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.
Ah, alright, so you use element for id, and index only for the tag, for the benefit of the tabview.
I would still consider using the same tag and id, and map it in the reducer, since in there you have access to both the elements and their offsets.
|
||
private var positionIndicator: some SwiftUI.View { | ||
HStack(spacing: spacing) { | ||
if store.cards.count > 1 { |
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.
Minor: maybe it should be outside of positionIndicator
, just to not add an empty HStack I guess
.padding([.top, .leading], .medium2) | ||
.padding(.trailing, trailingPadding) | ||
.padding(.bottom, .small1) | ||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) |
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 like a lot of padding + frame, is it really needed?
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.
Yes 🫤
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 makes it so that the frame grows as much as it can, instead of hugging the content. It's very similar to using say an HSTack with Spacer's.
Button(action: closeAction) { | ||
Image(asset: AssetResource.close) | ||
.resizable() | ||
.frame(width: .medium3, height: .medium3) | ||
.tint(.app.gray1) | ||
.padding(.small2) | ||
} |
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.
We have already close button, surprised that this is needed
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 close button is intended to be used in a toolbar so it has a bigger padding, which makes the X
overlap with the background/dapp image. That's why I changed it to this manual implementation.
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.
CloseButton does not seem to have any padding, but anyway would be nice to have a single definition of CloseButton if possible.
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.
CloseButton
defines a size of 44x44, meaning the asset (24x24) will have a 10 padding on each side.
In this case, we want the asset to be of 16x16 and to have a padding of 8 on top & trailing sides.
I can extend the CloseButton to adapt for each type.
} | ||
} | ||
|
||
private var trailingPadding: CGFloat { |
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.
Not quite getting what this is for, I guess I need to play with it.
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.
basically how much space we must leave empty from the trailing side of the view. Depending on its content, we have more or less space that we can use without the text overlapping with an image.
Original implementation had a fixed value, but that would leave very little space for some views on smaller devices (as iPhone SE) and content would look trimmed.
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 reason it was fixed is that the image we want to avoid covering is fixed size, because the view has a fixed height. That in turn is because tabview can't handle flexible heights very well.
I saw a question about the dummy views, though I can't find it anymore. Their purpose is to show the teased cards on the sides. The tabview itself does not reach the end of the screen, it is only as wide as the card, plus half the inter card spacing on each side. |
6c36818
to
800a610
Compare
if state.config.shouldSendWalletCreatedEvent { | ||
appEventsClient.handleEvent(.walletCreated) | ||
} |
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 event should be added when the onboarding is completed in App+Reducer, as by definition the onboarding happens when user creates a new Wallet.
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.
But in such case, it could be because the user restored a wallet
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.
Correct, then it would be better to have this code in OnboardingCoordinator when the CreateAccount completes.
Jira ticket: ABW-3439
Jira ticket: ABW-3443
Depends on radixdlt/sargon#180
Description
A carousel showing various action cards at the top of the home screen.