Skip to content

Add Wallet portfolio scene and chart/service refactor#1768

Open
gemdev111 wants to merge 15 commits intomainfrom
1705-portfolio-value
Open

Add Wallet portfolio scene and chart/service refactor#1768
gemdev111 wants to merge 15 commits intomainfrom
1705-portfolio-value

Conversation

@gemdev111
Copy link
Contributor

@gemdev111 gemdev111 commented Mar 4, 2026

Introduce a new WalletPortfolioScene and WalletPortfolioSceneViewModel with PortfolioService to fetch and combine asset charts using ChartService and PriceService. Add portfolio sheet presentation to WalletNavigationStack and a trigger in WalletScene/WalletSceneViewModel (isPresentingPortfolio, onSelectPortfolio). Refactor chart-related APIs: ChartValuesViewModel gains a static priceChange factory and priceModel, ChartView switches to selecting ChartDateValue, and PerpetualPortfolioSceneViewModel updated to use the new factory. Add Balance.total computed property and use it in BalanceViewModel; add subtitle action support to WalletHeaderView with a new system image constant. Update package dependencies to include PriceService and adjust test expectations accordingly.

Screenshot 1 Screenshot 2
Screenshot 3 Screenshot 4

closes #1705

Introduce a new WalletPortfolioScene and WalletPortfolioSceneViewModel with PortfolioService to fetch and combine asset charts using ChartService and PriceService. Add portfolio sheet presentation to WalletNavigationStack and a trigger in WalletScene/WalletSceneViewModel (isPresentingPortfolio, onSelectPortfolio). Refactor chart-related APIs: ChartValuesViewModel gains a static priceChange factory and priceModel, ChartView switches to selecting ChartDateValue, and PerpetualPortfolioSceneViewModel updated to use the new factory. Add Balance.total computed property and use it in BalanceViewModel; add subtitle action support to WalletHeaderView with a new system image constant. Update package dependencies to include PriceService and adjust test expectations accordingly.
@gemdev111 gemdev111 self-assigned this Mar 4, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new, dedicated portfolio scene within the wallet tab, enabling users to visualize a combined chart of their assets over various timeframes. This feature required the creation of new UI components, view models, and a service layer responsible for aggregating financial data from multiple sources. Concurrently, core charting components were refactored to enhance flexibility and data handling, and the main wallet view was updated to provide a clear entry point to this new portfolio functionality.

Highlights

  • New Wallet Portfolio Scene: A new WalletPortfolioScene and WalletPortfolioSceneViewModel have been introduced, providing a dedicated view for users to see a combined chart of their asset portfolio.
  • Portfolio Service for Chart Aggregation: A new PortfolioService was developed to fetch individual asset charts using ChartService and PriceService, then combine them into a single, comprehensive portfolio chart.
  • Chart API Refactoring: Chart-related APIs were refactored, including the addition of a static priceChange factory method to ChartValuesViewModel and updating ChartView to directly select ChartDateValue elements for improved data handling.
  • Wallet Integration and Navigation: The new portfolio scene is integrated into the WalletNavigationStack with a sheet presentation, and a trigger (onSelectPortfolio) has been added to WalletScene and WalletSceneViewModel to access it.
  • Balance Total Property: A total computed property was added to the Balance struct, simplifying the aggregation of all balance components (available, frozen, locked, staked, pending, rewards, earn).
  • Enhanced Wallet Header View: WalletHeaderView now supports an optional subtitle action, allowing for interactive elements like navigating to the new portfolio scene, indicated by a new system image.
  • Dependency Updates: The WalletTab package dependencies were updated to include PriceService and adjust test expectations accordingly.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Features/Perpetuals/Sources/ViewModels/PerpetualPortfolioSceneViewModel.swift
    • Updated chart values creation to use the new static priceChange factory method from ChartValuesViewModel.
  • Features/WalletTab/Package.swift
    • Added PriceService as a dependency for the WalletTab feature.
    • Removed PriceServiceTestKit from WalletTabTestKit dependencies.
  • Features/WalletTab/Sources/Scenes/WalletPortfolioScene.swift
    • Added a new SwiftUI View for displaying the wallet portfolio, including a ChartStateView and period selection.
  • Features/WalletTab/Sources/Scenes/WalletScene.swift
    • Modified WalletHeaderView initialization to include the new onSubtitleAction parameter.
  • Features/WalletTab/Sources/Services/PortfolioService.swift
    • Added a new PortfolioService struct responsible for fetching individual asset charts and combining them into a single portfolio chart.
  • Features/WalletTab/Sources/ViewModels/WalletPortfolioSceneViewModel.swift
    • Added a new WalletPortfolioSceneViewModel to manage the state and business logic for the WalletPortfolioScene, including fetching and processing chart data.
  • Features/WalletTab/Sources/ViewModels/WalletSceneViewModel.swift
    • Added a new isPresentingPortfolio state variable and an onSelectPortfolio action to trigger the presentation of the portfolio scene.
    • Reordered imports for Store and Preferences.
  • Gem/Navigation/Wallet/WalletNavigationStack.swift
    • Imported PriceService.
    • Added a .sheet modifier to present the WalletPortfolioScene when isPresentingPortfolio is true.
  • Gem/Views/MainTabView.swift
    • Removed the Store import.
  • Packages/Primitives/Sources/Extensions/Balance+Primitives.swift
    • Added a total computed property to the Balance struct, summing all balance components.
  • Packages/PrimitivesComponents/Sources/Components/WalletHeaderView.swift
    • Introduced an optional onSubtitleAction parameter to the initializer.
    • Refactored the subtitle display to support a tappable action with an optional chart icon.
  • Packages/PrimitivesComponents/Sources/ViewModels/BalanceViewModel.swift
    • Updated the total computed property to use the new balance.total extension.
  • Packages/PrimitivesComponents/Sources/ViewModels/ChartValuesViewModel.swift
    • Added a new static factory method priceChange for creating ChartValuesViewModel instances.
    • Adjusted the priceViewModel method to correctly calculate and display price changes based on the chart type.
  • Packages/PrimitivesComponents/Sources/Views/ChartView.swift
    • Changed the @State variable selectedValue to selectedElement of type ChartDateValue.
    • Updated chart rendering and drag gesture logic to work with ChartDateValue directly.
  • Packages/Style/Sources/SystemImage.swift
    • Added a new static constant chartLineUptrendXyaxis for a system image.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Wallet Portfolio feature and includes several refactorings for chart-related components. The changes are generally well-structured and follow good practices. I've identified a couple of areas for improvement: one related to simplifying the view's asynchronous logic for better robustness and another concerning a performance optimization in the portfolio calculation logic.

Comment on lines +57 to +59
let price = chart.prices
.min(by: { abs($0.timestamp - point.timestamp) < abs($1.timestamp - point.timestamp) })?
.value ?? .zero
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Finding the closest price by iterating through all prices with .min(by:) has a time complexity of O(N) for each point in the timeline. For large charts, this can be a performance bottleneck.

Assuming chart.prices is sorted by timestamp, you can optimize this search to O(log N) by implementing a binary search to find the price with the closest timestamp. This would make the combine function significantly more efficient.

Comment on lines +31 to +36
.task {
await model.fetch()
}
.onChange(of: model.selectedPeriod) {
Task { await model.fetch() }
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The combination of .task for the initial fetch and .onChange for subsequent fetches can be simplified and made more robust by using the .task(id:) modifier. This modifier automatically handles cancellation and re-running the task when the identified value changes, and it also runs the task when the view first appears. This would replace both the .task and .onChange blocks with a single, cleaner block of code.

Suggested change
.task {
await model.fetch()
}
.onChange(of: model.selectedPeriod) {
Task { await model.fetch() }
}
.task(id: model.selectedPeriod) {
await model.fetch()
}

Remove the old local PortfolioService and add a new public PortfolioService in Packages/FeatureServices/PriceService that delegates to GemAPIPortfolioService. Update WalletPortfolioSceneViewModel to accept an injected PortfolioService and use it to fetch portfolio assets, while obtaining exchange rates from PriceService to build chart values. Wire the new service through the app: add portfolioService to Environment, AppResolver, and ServicesFactory (constructed with apiService), and pass it into WalletNavigationStack when creating the view model. These changes centralize portfolio fetching via the shared PriceService package and enable DI across the app.
Render portfolio all-time high/low and introduce view model to format them. Changes: add AllTimeValueViewModel to format all-time high/low list items; update WalletPortfolioScene to display a new Section using model.allTimeValues; change WalletPortfolioSceneViewModel state from chart array to PortfolioAssets, update fetch to store the full PortfolioAssets result, add allTimeValues computed property, wire AllTimeValueViewModel, and adjust chartModel to build charts from PortfolioAssets (using price service rate with a 1.0 fallback).
@gemdev111 gemdev111 marked this pull request as ready for review March 5, 2026 16:52
Introduce an optional subtitleImage (Image?) on HeaderViewModel with a default nil implementation. Update WalletHeaderView to render model.subtitleImage when present instead of checking onSubtitleAction and using a hardcoded system image. Implement subtitleImage in WalletHeaderViewModel (now returning chartLineUptrendXyaxis) and remove the previous pnl-based switch and subtitleImageOffset property to simplify image handling.
Introduce WalletPortfolioData to bundle chart and portfolio assets, and update WalletPortfolioSceneViewModel to use it as state. Pass Wallet into the view model to use the wallet name for the navigation title. Move chart construction into fetch() (apply price rate, build ChartValuesViewModel) and set state to include the combined WalletPortfolioData. Replace the previous private chartModel helper and adjust formatter usage: WalletPortfolioSceneViewModel now creates CurrencyFormatter instances for price and percent and uses the refactored AllTimeValueViewModel which now accepts injected formatters. Update WalletNavigationStack to pass the wallet and adapt to the new AllTimeValueViewModel initializer signature.
Refactor chart header and list handling: add ChartListView and ChartListViewable protocol to centralize the chart header UI and fetching logic. Rename ChartPriceViewModel/ChartPriceView to ChartHeaderViewModel/ChartHeaderView and add optional headerValue to support showing a primary header amount. Update ChartValuesViewModel to carry headerValue and provide header view models; adapt ChartView and CandlestickChartView to use ChartHeaderView. Make scene view models conform to ChartListViewable, wire ObservableQuery for live assets/total in WalletPortfolioSceneViewModel, and replace inline list headers with ChartListView in several scenes. Update testkit and tests (add ChartHeaderViewModelTests, update mocks), and remove the old ChartPriceViewModelTests. Also remove the now-unused assets parameter from WalletNavigationStack.
Clean up unused imports and dead code: remove the `Style` import from several Swift files and the `Charts` import from ChartScene.swift, and delete unused localized title properties (`emptyTitle`, `errorTitle`) from ChartSceneViewModel. This reduces compiler warnings and tidies MarketInsight, Perpetuals, and WalletTab feature modules.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Portfolio value

1 participant