-
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
Changes from 21 commits
81bf003
c1ddf49
39a880b
ca0373a
5ce4ed4
da8f3d7
af2b0f2
6cb0b7e
dc784df
4939e65
0072003
dc98d28
489c66b
b62daca
1060d2c
5fac7ed
2a6e024
24b0205
84dc42e
08ddac9
1ac02ee
0b0479e
a79a0f5
179daae
3f4f83d
800a610
b0f8ac4
345d591
4b927fc
7c05410
0bf524f
50da244
6f9f6f0
1be9f16
3d3c477
0cab947
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// MARK: - AppEventsClient | ||
public struct AppEventsClient: Sendable { | ||
public var handleEvent: HandleEvent | ||
|
||
init(handleEvent: @escaping HandleEvent) { | ||
self.handleEvent = handleEvent | ||
} | ||
} | ||
|
||
// MARK: AppEventsClient.HandleEvent | ||
extension AppEventsClient { | ||
public typealias HandleEvent = @Sendable (AppEvent) -> Void | ||
} | ||
|
||
extension DependencyValues { | ||
public var appEventsClient: AppEventsClient { | ||
get { self[AppEventsClient.self] } | ||
set { self[AppEventsClient.self] = newValue } | ||
} | ||
} | ||
|
||
// MARK: - AppEvent | ||
public enum AppEvent: Sendable, Hashable { | ||
case appStarted | ||
case walletCreated | ||
case deepLinkReceived(String) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
extension AppEventsClient: DependencyKey { | ||
public static let liveValue: AppEventsClient = { | ||
@Dependency(\.homeCardsClient) var homeCardsClient | ||
|
||
return .init( | ||
handleEvent: { event in | ||
switch event { | ||
case .appStarted: | ||
homeCardsClient.walletStarted() | ||
case .walletCreated: | ||
homeCardsClient.walletCreated() | ||
case let .deepLinkReceived(value): | ||
homeCardsClient.deepLinkReceived(value) | ||
} | ||
} | ||
) | ||
}() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// MARK: - AppEventsClient + TestDependencyKey | ||
extension AppEventsClient: TestDependencyKey { | ||
public static let previewValue = Self.noop | ||
|
||
public static let testValue = Self( | ||
handleEvent: unimplemented("\(Self.self).handleEvent") | ||
) | ||
} | ||
|
||
extension AppEventsClient { | ||
public static let noop = Self( | ||
handleEvent: { _ in } | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,16 +37,13 @@ extension AppsFlyerClient: DependencyKey { | |
} | ||
|
||
private class Delegate: NSObject, DeepLinkDelegate, @unchecked Sendable { | ||
@Dependency(\.appEventsClient) var appEventsClient | ||
|
||
func didResolveDeepLink(_ result: DeepLinkResult) { | ||
if let deepLink = result.deepLink { | ||
loggerGlobal.info("did resolve deep link. Is deferred: \(deepLink.isDeferred). Click events: \(deepLink.clickEvent)") | ||
if deepLink.isDeferred { | ||
let message = if let deepLinkValue = deepLink.clickEvent["deep_link_value"] as? String { | ||
"Resolved deferred DL with value \(deepLinkValue)" | ||
} else { | ||
"Resolved deferred DL without value" | ||
} | ||
AppsFlyerLib.shared().logEvent(message, withValues: deepLink.clickEvent) | ||
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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. mm I am not sure I prefer it this way, since |
||
} | ||
} else if let error = result.error { | ||
loggerGlobal.info("failed to resolve deep link. Status: \(result.status), Error: \(error.localizedDescription)") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// MARK: - HomeCardsClient | ||
public struct HomeCardsClient: Sendable { | ||
public var cards: Cards | ||
public var removeCard: RemoveCard | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. these should express some command, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ignored as per changes to AppEventClient |
||
|
||
init( | ||
cards: @escaping Cards, | ||
removeCard: @escaping RemoveCard, | ||
walletStarted: @escaping WalletStarted, | ||
walletCreated: @escaping WalletCreated, | ||
deepLinkReceived: @escaping DeepLinkReceived | ||
) { | ||
self.cards = cards | ||
self.removeCard = removeCard | ||
self.walletStarted = walletStarted | ||
self.walletCreated = walletCreated | ||
self.deepLinkReceived = deepLinkReceived | ||
} | ||
} | ||
|
||
extension HomeCardsClient { | ||
public typealias Cards = @Sendable () -> AnyAsyncSequence<[HomeCard]> | ||
public typealias RemoveCard = @Sendable (HomeCard) -> Void | ||
public typealias WalletStarted = @Sendable () -> Void | ||
public typealias WalletCreated = @Sendable () -> Void | ||
public typealias DeepLinkReceived = @Sendable (String) -> Void | ||
} | ||
|
||
extension DependencyValues { | ||
public var homeCardsClient: HomeCardsClient { | ||
get { self[HomeCardsClient.self] } | ||
set { self[HomeCardsClient.self] = newValue } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import ComposableArchitecture | ||
|
||
// MARK: - HomeCardsClient + DependencyKey | ||
extension HomeCardsClient: DependencyKey { | ||
public static let liveValue: Self = { | ||
let observer = HomeCardsObserver() | ||
|
||
// We are hardcoding to `.mainnet` because the cards are currently gateway agnostic. In the future, when Profile is integrated into Sargon, it will be Sargon | ||
// observing the current gateway and defining the networkId to use. | ||
let manager = HomeCardsManager(networkAntenna: URLSession.shared, networkId: .mainnet, cardsStorage: HomeCardsStorage(), observer: observer) | ||
|
||
return Self( | ||
cards: { | ||
observer.subject.eraseToAnyAsyncSequence() | ||
}, | ||
removeCard: { card in | ||
Task { | ||
try? await manager.cardDismissed(card: card) | ||
} | ||
}, | ||
walletStarted: { | ||
Task { | ||
try? await manager.walletStarted() | ||
} | ||
}, | ||
walletCreated: { | ||
Task { | ||
try? await manager.walletCreated() | ||
} | ||
}, | ||
deepLinkReceived: { value in | ||
Task { | ||
try? await manager.deepLinkReceived(encodedValue: value) | ||
} | ||
} | ||
) | ||
}() | ||
} | ||
|
||
// MARK: - HomeCardsManager + Sendable | ||
extension HomeCardsManager: @unchecked Sendable {} | ||
|
||
// MARK: - HomeCardsStorage | ||
private final class HomeCardsStorage: Sargon.HomeCardsStorage { | ||
@Dependency(\.userDefaults) var userDefaults | ||
GhenadieVP marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
func saveCards(encodedCards: Data) async throws { | ||
userDefaults.setHomeCards(encodedCards) | ||
} | ||
|
||
func loadCards() async throws -> Data? { | ||
userDefaults.getHomeCards() | ||
} | ||
} | ||
|
||
// MARK: - HomeCardsObserver | ||
private final class HomeCardsObserver: Sargon.HomeCardsObserver, Sendable { | ||
let subject: AsyncCurrentValueSubject<[HomeCard]> = .init([]) | ||
|
||
func handleCardsUpdate(cards: [HomeCard]) { | ||
subject.send(cards) | ||
} | ||
} |
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 theAppEventsClient
on its initialization, to make sure theHomeCardsClient
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 theHomeCardsClient
(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