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 all 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 @@ -66,6 +66,9 @@ extension AccountPortfoliosClient: DependencyKey {

let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses).map(\.nonEmptyVaults)

let portfolios = accounts.map { AccountPortfolio(account: $0) }
await state.handlePortfoliosUpdate(portfolios)

/// Put together all resources from already fetched and new accounts
let currentAccounts = state.portfoliosSubject.value.wrappedValue.map { $0.values.map(\.account) } ?? []
let allResources: [ResourceAddress] = {
Expand Down Expand Up @@ -116,9 +119,6 @@ extension AccountPortfoliosClient: DependencyKey {
}
}

let portfolios = accounts.map { AccountPortfolio(account: $0) }
await state.handlePortfoliosUpdate(portfolios)

// Load additional details
_ = await accounts.parallelMap(fetchPoolAndStakeUnitsDetails)

Expand All @@ -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 @@ -5,13 +5,15 @@ extension AccountPortfoliosClient: TestDependencyKey {
public static let testValue = AccountPortfoliosClient(
fetchAccountPortfolios: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolios"),
fetchAccountPortfolio: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolio"),
portfolioUpdates: unimplemented("\(AccountPortfoliosClient.self).fetchAccountPortfolio"),
portfolioForAccount: unimplemented("\(AccountPortfoliosClient.self).portfolioForAccount"),
portfolios: unimplemented("\(AccountPortfoliosClient.self).portfolios")
)

public static let noop = AccountPortfoliosClient(
fetchAccountPortfolios: { _, _ in throw NoopError() },
fetchAccountPortfolio: { _, _ in throw NoopError() },
portfolioUpdates: { AsyncLazySequence([]).eraseToAnyAsyncSequence() },
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
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ extension TokenPricesClient.TokenPrices {
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = Int(RETDecimal.maxDivisibility)
formatter.roundingMode = .down
formatter.decimalSeparator = "." // Enfore dot notation for RETDecimal

self = tokenPricesResponse.tokens.reduce(into: [:]) { partialResult, next in
let trimmed = formatter.string(for: next.price) ?? ""
Expand Down
29 changes: 23 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 @@ -292,6 +292,10 @@ extension Loadable {
concat(other).map(join)
}

public func reduce<Element>(_ join: (Element) -> Void) -> Void where Value == Array<Element> {

}

public mutating func mutateValue(_ mutate: (inout Value) -> Void) {
switch self {
case .idle, .loading, .failure:
Expand All @@ -307,16 +311,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
12 changes: 8 additions & 4 deletions RadixWallet/Features/AssetsFeature/AssetsView+Reducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public struct AssetsView: Sendable, FeatureReducer {
return nil
}

return .init(sections: sections)
return .init(sections: sections, destination: state.resources.fungibleTokenList?.destination)
}()

state.accountPortfolio.refresh(from: .success(portfolio))
Expand All @@ -210,7 +210,8 @@ public struct AssetsView: Sendable, FeatureReducer {
},
isSelected: mode.nonXrdRowSelected(poolUnit.resource.resourceAddress)
)
}.asIdentifiable()
}.asIdentifiable(),
destination: state.resources.poolUnitsList?.destination
)

let stakes = portfolio.account.poolUnitResources.radixNetworkStakes
Expand All @@ -234,13 +235,14 @@ public struct AssetsView: Sendable, FeatureReducer {
dict[resource] = selectedtokens
}
} : nil,
stakeUnitDetails: state.accountPortfolio.stakeUnitDetails.flatten()
stakeUnitDetails: state.accountPortfolio.stakeUnitDetails.flatten(),
destination: state.resources.stakeUnitList?.destination
)

state.totalFiatWorth.refresh(from: portfolio.totalFiatWorth)
state.resources = .init(
fungibleTokenList: fungibleTokenList,
nonFungibleTokenList: !nfts.isEmpty ? .init(rows: .init(uniqueElements: nfts)) : nil,
nonFungibleTokenList: !nfts.isEmpty ? .init(rows: nfts.asIdentifiable(), destination: state.resources.nonFungibleTokenList?.destination) : nil,
stakeUnitList: stakeUnitList,
poolUnitsList: poolUnitList
)
Expand All @@ -250,6 +252,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 @@ -4,16 +4,10 @@ import SwiftUI
// MARK: - FungibleAssetList
public struct FungibleAssetList: Sendable, FeatureReducer {
public struct State: Sendable, Hashable {
public var sections: IdentifiedArrayOf<FungibleAssetList.Section.State>
public var sections: IdentifiedArrayOf<FungibleAssetList.Section.State> = []

@PresentationState
public var destination: Destination.State?

public init(
sections: IdentifiedArrayOf<FungibleAssetList.Section.State> = []
) {
self.sections = sections
}
}

@CasePathable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ extension ResourceBalanceView {
}
.disabled(onTap == nil)
.buttonStyle(.borderless)
.roundedCorners(strokeColor: .red) // .app.gray3
.roundedCorners(strokeColor: .app.gray3)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ public struct NonFungibleAssetList: Sendable, FeatureReducer {

@PresentationState
public var destination: Destination.State?

public init(rows: IdentifiedArrayOf<NonFungibleAssetList.Row.State>) {
self.rows = rows
}
}

public enum ChildAction: Sendable, Equatable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ struct ValidatorStakeView: View {
onTap: onTap,
onClaimAllTapped: onClaimAllTapped
)
.padding(.small1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ public struct StakeUnitList: Sendable, FeatureReducer {
account: OnLedgerEntity.Account,
selectedLiquidStakeUnits: IdentifiedArrayOf<OnLedgerEntity.OwnedFungibleResource>?,
selectedStakeClaimTokens: SelectedStakeClaimTokens?,
stakeUnitDetails: Loadable<IdentifiedArrayOf<OnLedgerEntitiesClient.OwnedStakeDetails>>
stakeUnitDetails: Loadable<IdentifiedArrayOf<OnLedgerEntitiesClient.OwnedStakeDetails>>,
destination: Destination.State? = nil
) {
self.account = account
self.selectedLiquidStakeUnits = selectedLiquidStakeUnits
self.selectedStakeClaimTokens = selectedStakeClaimTokens
self.destination = destination

switch stakeUnitDetails {
case .idle, .loading:
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 @@ -46,21 +47,10 @@ extension Home {
public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action> {
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) {
guard !Task.isCancelled else {
return
}
await send(.internal(.accountPortfolioUpdate(accountPortfolio)))
}
}
return .none

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

Expand All @@ -74,23 +64,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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extension Home.State {
.init(
hasNotification: shouldWriteDownPersonasSeedPhrase,
showRadixBanner: showRadixBanner,
totalFiatWorth: totalFiatWorth
totalFiatWorth: showFiatWorth ? totalFiatWorth : nil
)
}
}
Expand Down
Loading
Loading