Skip to content
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

Home Screen load laggines #1053

Merged
merged 10 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public struct AccountPortfoliosClient: Sendable {
/// Will return the portfolio after fetch, as well will notify any subscribes through `portfolioForAccount`
public var fetchAccountPortfolio: FetchAccountPortfolio

public var portfolioUpdates: PortfolioUpdates

/// Subscribe to portfolio changes for a given account address
public var portfolioForAccount: PortfolioForAccount

Expand All @@ -21,6 +23,8 @@ extension AccountPortfoliosClient {
public typealias FetchAccountPortfolio = @Sendable (_ address: AccountAddress, _ forceResfresh: Bool) async throws -> AccountPortfolio
public typealias FetchAccountPortfolios = @Sendable (_ addresses: [AccountAddress], _ forceResfresh: Bool) async throws -> [AccountPortfolio]
public typealias PortfolioForAccount = @Sendable (_ address: AccountAddress) async -> AnyAsyncSequence<AccountPortfolio>

public typealias PortfolioUpdates = @Sendable () -> AnyAsyncSequence<Loadable<[AccountPortfolio]>>
public typealias Portfolios = @Sendable () -> [AccountPortfolio]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ extension AccountPortfoliosClient: DependencyKey {

return portfolio
},
portfolioUpdates: {
state.portfoliosSubject
.map { $0.map { Array($0.values) } }
.eraseToAnyAsyncSequence()
},
portfolioForAccount: { address in
await state.portfolioForAccount(address)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ extension AccountPortfoliosClient: TestDependencyKey {

public static let testValue = AccountPortfoliosClient(
fetchAccountPortfolios: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolios"),
fetchAccountPortfolio: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolio"),
fetchAccountPortfolio: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolio"), portfolioUpdates: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolio"),
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
portfolioForAccount: unimplemented("\(AccountPortfoliosClient.self).portfolioForAccount"),
portfolios: unimplemented("\(AccountPortfoliosClient.self).portfolios")
)

public static let noop = AccountPortfoliosClient(
fetchAccountPortfolios: { _, _ in throw NoopError() },
fetchAccountPortfolio: { _, _ in throw NoopError() },
portfolioUpdates: { fatalError() },
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
portfolioForAccount: { _ in fatalError() },
portfolios: { fatalError() }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension AccountPortfoliosClient.State {
}

func portfolioForAccount(_ address: AccountAddress) -> AnyAsyncSequence<AccountPortfoliosClient.AccountPortfolio> {
portfoliosSubject.compactMap { $0[address].unwrap()?.wrappedValue }.eraseToAnyAsyncSequence()
portfoliosSubject.compactMap { $0[address].unwrap()?.wrappedValue }.removeDuplicates().eraseToAnyAsyncSequence()
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
}

private func setOrUpdateAccountPortfolio(_ portfolio: AccountPortfoliosClient.AccountPortfolio) {
Expand Down
25 changes: 19 additions & 6 deletions RadixWallet/Core/FeaturePrelude/Loadable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ extension Loadable {
}
}

public func firstd(where predicate: (Value.Element) -> Bool) -> Loadable<Value.Element?> where Value: Sequence {
public func first(where predicate: (Value.Element) -> Bool) -> Loadable<Value.Element?> where Value: Sequence {
switch self {
case .idle:
return .idle
Expand Down Expand Up @@ -307,16 +307,29 @@ extension Loadable {
public mutating func refresh(
from other: Loadable<Value>,
valueChangeMap: (_ old: Value, _ new: Value) -> Value = { _, new in new }
) {
) where Value: Equatable {
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
switch (self, other) {
// If `other` is success, update the content regardless of the current state
case let (.success(oldValue), .success(newValue)):
self = .success(valueChangeMap(oldValue, newValue))
case let (_, .success(otherValue)):
// Update to success if no current value
case let (.idle, .success(otherValue)),
let (.loading, .success(otherValue)),
let (.failure, .success(otherValue)):

self = .success(otherValue)

// Update to new value only if it changed
case let (.success(oldValue), .success(newValue)):
if oldValue != newValue {
self = .success(valueChangeMap(oldValue, newValue))
}

// If current state is success, don't update if `other` is loading or failed
case (.success, _):
break

case (.loading, .loading),
(.idle, .idle):
break

// If current state is other than .success
case let (_, other):
self = other
Expand Down
2 changes: 2 additions & 0 deletions RadixWallet/Features/AssetsFeature/AssetsView+Reducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ public struct AssetsView: Sendable, FeatureReducer {
extension AccountPortfoliosClient.AccountPortfolio {
mutating func refresh(from portfolio: AccountPortfoliosClient.AccountPortfolio) {
self.account = portfolio.account
self.isCurrencyAmountVisible = portfolio.isCurrencyAmountVisible
self.fiatCurrency = portfolio.fiatCurrency
self.stakeUnitDetails.refresh(from: portfolio.stakeUnitDetails)
self.poolUnitDetails.refresh(from: portfolio.poolUnitDetails)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ extension Home {
public var id: AccountAddress { account.address }
public var accountWithInfo: AccountWithInfo

public var portfolio: Loadable<AccountPortfoliosClient.AccountPortfolio>
public var accountWithResources: Loadable<OnLedgerEntity.Account>
public var showFiatWorth: Bool = true
public var totalFiatWorth: Loadable<FiatWorth>

public init(
account: Profile.Network.Account
) {
self.accountWithInfo = .init(account: account)
self.portfolio = .loading
self.accountWithResources = .loading
self.totalFiatWorth = .loading
}
}
Expand All @@ -29,7 +29,8 @@ extension Home {
}

public enum InternalAction: Sendable, Equatable {
case accountPortfolioUpdate(AccountPortfoliosClient.AccountPortfolio)
case accountUpdated(OnLedgerEntity.Account)
case fiatWorthUpdated(Loadable<FiatWorth>)
case checkAccountAccessToMnemonic
}

Expand All @@ -47,20 +48,17 @@ extension Home {
switch viewAction {
case .task:
let accountAddress = state.account.address
if state.portfolio.wrappedValue == nil {
state.portfolio = .loading
}

self.checkAccountAccessToMnemonic(state: &state)

return .run { send in
for try await accountPortfolio in await accountPortfoliosClient.portfolioForAccount(accountAddress) {
for try await fiatWorth in await accountPortfoliosClient.portfolioForAccount(accountAddress).map(\.totalFiatWorth).removeDuplicates() {
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
guard !Task.isCancelled else {
return
}
await send(.internal(.accountPortfolioUpdate(accountPortfolio)))
await send(.internal(.fiatWorthUpdated(fiatWorth)))
}
}

case .exportMnemonicButtonTapped:
return .send(.delegate(.exportMnemonic))

Expand All @@ -74,23 +72,26 @@ extension Home {

public func reduce(into state: inout State, internalAction: InternalAction) -> Effect<Action> {
switch internalAction {
case let .accountPortfolioUpdate(portfolio):
state.isDappDefinitionAccount = portfolio.account.metadata.accountType == .dappDefinition
case let .accountUpdated(account):
assert(account.address == state.account.address)

assert(portfolio.account.address == state.account.address)
state.isDappDefinitionAccount = account.metadata.accountType == .dappDefinition
state.accountWithResources.refresh(from: .success(account))

state.portfolio = .success(portfolio)
state.totalFiatWorth.refresh(from: portfolio.totalFiatWorth)
return .send(.internal(.checkAccountAccessToMnemonic))

case .checkAccountAccessToMnemonic:
checkAccountAccessToMnemonic(state: &state)
return .none

case let .fiatWorthUpdated(fiatWorth):
state.totalFiatWorth.refresh(from: fiatWorth)
return .none
}
}

private func checkAccountAccessToMnemonic(state: inout State) {
state.checkAccountAccessToMnemonic(portfolio: state.portfolio.account.wrappedValue)
state.checkAccountAccessToMnemonic(portfolio: state.accountWithResources.wrappedValue)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ extension Home.AccountRow {
self.appearanceID = state.account.appearanceID
self.showFiatWorth = state.showFiatWorth
self.fiatWorth = state.totalFiatWorth
self.isLoadingResources = state.portfolio.isLoading
self.isLoadingResources = state.accountWithResources.isLoading

self.tag = .init(state: state)
self.isLedgerAccount = state.isLedgerAccount

self.mnemonicHandlingCallToAction = state.mnemonicHandlingCallToAction

// Resources
guard let portfolio = state.portfolio.wrappedValue else {
guard let accountWithResources = state.accountWithResources.wrappedValue else {
self.fungibleResourceIcons = []
self.nonFungibleResourcesCount = 0
self.stakedValidatorsCount = 0
Expand All @@ -64,13 +64,13 @@ extension Home.AccountRow {
return
}

let fungibleResources = portfolio.account.fungibleResources
let fungibleResources = accountWithResources.fungibleResources
let xrdIcon: [Thumbnail.TokenContent] = fungibleResources.xrdResource != nil ? [.xrd] : []
let otherIcons: [Thumbnail.TokenContent] = fungibleResources.nonXrdResources.map { .other($0.metadata.iconURL) }
self.fungibleResourceIcons = xrdIcon + otherIcons
self.nonFungibleResourcesCount = portfolio.account.nonFungibleResources.count
self.stakedValidatorsCount = portfolio.account.poolUnitResources.radixNetworkStakes.count
self.poolUnitsCount = portfolio.account.poolUnitResources.poolUnits.count
self.nonFungibleResourcesCount = accountWithResources.nonFungibleResources.count
self.stakedValidatorsCount = accountWithResources.poolUnitResources.radixNetworkStakes.count
self.poolUnitsCount = accountWithResources.poolUnitResources.poolUnits.count
}
}

Expand Down
19 changes: 19 additions & 0 deletions RadixWallet/Features/HomeFeature/Coordinator/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public struct Home: Sendable, FeatureReducer {
case loadedShouldWriteDownPersonasSeedPhrase(Bool)
case currentGatewayChanged(to: Radix.Gateway)
case shouldShowNPSSurvey(Bool)
case accountsResourcesLoaded(Loadable<[OnLedgerEntity.Account]>)
}

public enum ChildAction: Sendable, Equatable {
Expand Down Expand Up @@ -151,6 +152,7 @@ public struct Home: Sendable, FeatureReducer {
.merge(with: loadShouldWriteDownPersonasSeedPhrase())
.merge(with: loadGateways())
.merge(with: loadNPSSurveyStatus())
.merge(with: loadAccountResources())

case .createAccountButtonTapped:
state.destination = .createAccount(
Expand Down Expand Up @@ -206,6 +208,14 @@ public struct Home: Sendable, FeatureReducer {
errorQueue.schedule(error)
return .none

case let .accountsResourcesLoaded(accountsResources):
state.accountRows.mutateAll { row in
if let accountResources = accountsResources.first(where: { $0.address == row.id }).unwrap() {
row.accountWithResources.refresh(from: accountResources)
}
}
return .none

case let .loadedShouldWriteDownPersonasSeedPhrase(shouldBackup):
state.shouldWriteDownPersonasSeedPhrase = shouldBackup
return .none
Expand Down Expand Up @@ -372,6 +382,15 @@ public struct Home: Sendable, FeatureReducer {
await npsSurveyClient.uploadUserFeedback(feedback)
}
}

private func loadAccountResources() -> Effect<Action> {
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved
.run { send in
for try await accountResources in accountPortfoliosClient.portfolioUpdates().map { $0.map { $0.map(\.account) } }.removeDuplicates() {
guard !Task.isCancelled else { return }
await send(.internal(.accountsResourcesLoaded(accountResources)))
}
}
}
}

extension Home.State {
Expand Down
Loading