diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Vault/Landing/dydxVaultViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Vault/Landing/dydxVaultViewBuilder.swift index b58975f8..63209939 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/Vault/Landing/dydxVaultViewBuilder.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Vault/Landing/dydxVaultViewBuilder.swift @@ -91,7 +91,7 @@ private class dydxVaultViewBuilderPresenter: HostedViewPresenter dydxVaultPositionViewModel? in + let newPositions = vault?.positions?.positions?.map { (position) -> dydxVaultPositionViewModel? in guard let leverage = position.currentLeverageMultiple?.doubleValue, let marketId = position.marketId, // special case for fake USDC market to show unused margin @@ -110,20 +110,40 @@ private class dydxVaultViewBuilderPresenter: HostedViewPresenter 0 ? .long : .short, - leverage: leverage, - equity: equity, - notionalValue: notionalValue, - positionSize: positionSize.magnitude, - tokenUnitPrecision: tokenUnitPrecision, - pnlAmount: position.thirtyDayPnl?.absolute?.doubleValue, - pnlPercentage: position.thirtyDayPnl?.percent?.doubleValue, - sparklineValues: position.thirtyDayPnl?.sparklinePoints?.map({ $0.doubleValue })) + + // only create new view model instance if it does not already exist + if let existing = viewModel?.positions?[marketId] { + return existing.updated( + marketId: marketId, + displayId: displayId, + iconType: iconType, + side: positionSize > 0 ? .long : .short, + leverage: leverage, + equity: equity, + notionalValue: notionalValue, + positionSize: positionSize.magnitude, + tokenUnitPrecision: tokenUnitPrecision, + pnlAmount: position.thirtyDayPnl?.absolute?.doubleValue, + pnlPercentage: position.thirtyDayPnl?.percent?.doubleValue, + sparklineValues: position.thirtyDayPnl?.sparklinePoints?.map({ $0.doubleValue })) + } else { + return dydxVaultPositionViewModel( + marketId: marketId, + displayId: displayId, + iconType: iconType, + side: positionSize > 0 ? .long : .short, + leverage: leverage, + equity: equity, + notionalValue: notionalValue, + positionSize: positionSize.magnitude, + tokenUnitPrecision: tokenUnitPrecision, + pnlAmount: position.thirtyDayPnl?.absolute?.doubleValue, + pnlPercentage: position.thirtyDayPnl?.percent?.doubleValue, + sparklineValues: position.thirtyDayPnl?.sparklinePoints?.map({ $0.doubleValue })) + } } .compactMap { $0 } - .sorted(by: { $0.equity > $1.equity }) + viewModel?.positions = Dictionary(uniqueKeysWithValues: newPositions?.map({ ( $0.marketId, $0) }) ?? []) } private func updateChartState(vault: Abacus.Vault?, valueType: dydxVaultChartViewModel.ValueTypeOption, timeType: dydxVaultChartViewModel.ValueTimeOption) { diff --git a/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultPositionView.swift b/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultPositionView.swift index 3ad8977b..b75ae585 100644 --- a/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultPositionView.swift +++ b/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultPositionView.swift @@ -11,9 +11,12 @@ import PlatformUI import Utilities import DGCharts import dydxFormatter +import SDWebImage +import SDWebImageSwiftUI public class dydxVaultPositionViewModel: PlatformViewModel { + @Published public var marketId: String @Published public var displayId: String @Published public var iconType: PlatformIconViewModel.IconType = .init(url: nil, placeholderText: nil) @Published public var side: SideTextViewModel.Side @@ -62,7 +65,35 @@ public class dydxVaultPositionViewModel: PlatformViewModel { dydxFormatter.shared.percent(number: pnlPercentage, digits: 2) ?? "--" } + public func updated(marketId: String, + displayId: String, + iconType: PlatformIconViewModel.IconType, + side: SideTextViewModel.Side, + leverage: Double, + equity: Double, + notionalValue: Double, + positionSize: Double, + tokenUnitPrecision: Int, + pnlAmount: Double?, + pnlPercentage: Double?, + sparklineValues: [Double]?) -> dydxVaultPositionViewModel { + self.marketId = marketId + self.displayId = displayId + self.iconType = iconType + self.side = side + self.leverage = leverage + self.equity = equity + self.notionalValue = notionalValue + self.positionSize = positionSize + self.tokenUnitPrecision = tokenUnitPrecision + self.pnlAmount = pnlAmount + self.pnlPercentage = pnlPercentage + self.sparklineValues = sparklineValues + return self + } + public init( + marketId: String, displayId: String, iconType: PlatformIconViewModel.IconType, side: SideTextViewModel.Side, @@ -74,6 +105,7 @@ public class dydxVaultPositionViewModel: PlatformViewModel { pnlAmount: Double?, pnlPercentage: Double?, sparklineValues: [Double]?) { + self.marketId = marketId self.displayId = displayId self.iconType = iconType self.side = side @@ -87,7 +119,7 @@ public class dydxVaultPositionViewModel: PlatformViewModel { self.sparklineValues = sparklineValues } - public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView { + public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformUI.PlatformView { PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] _ in guard let self = self else { return AnyView(PlatformView.nilView) } return VaultPositionView(viewModel: self) diff --git a/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultViewModel.swift b/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultViewModel.swift index 6384e837..7eaaf0c0 100644 --- a/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultViewModel.swift +++ b/dydx/dydxViews/dydxViews/_v4/Vault/Landing/dydxVaultViewModel.swift @@ -17,12 +17,16 @@ public class dydxVaultViewModel: PlatformViewModel { @Published public var thirtyDayReturnPercent: Double? @Published public var totalValueLocked: Double? @Published public var vaultChart: dydxVaultChartViewModel? - @Published public var positions: [dydxVaultPositionViewModel]? + @Published public var positions: [String: dydxVaultPositionViewModel]? @Published public var cancelAction: (() -> Void)? @Published public var learnMoreAction: (() -> Void)? @Published public var withdrawAction: (() -> Void)? @Published public var depositAction: (() -> Void)? + fileprivate var sortedPositions: [dydxVaultPositionViewModel] { + positions?.values.sorted(by: { $0.equity > $1.equity }) ?? [] + } + public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView { PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] _ in guard let self = self else { return AnyView(PlatformView.nilView) } @@ -256,7 +260,7 @@ private struct dydxVaultView: View { } private var positionsList: some View { - ForEach(viewModel.positions ?? [], id: \.id) { position in + ForEach(viewModel.sortedPositions, id: \.id) { position in position.createView() .centerAligned() .frame(height: 53)