-
Notifications
You must be signed in to change notification settings - Fork 58
Perp chart tooltip #1710
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
Merged
Merged
Perp chart tooltip #1710
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
62c4ddf
Add OHLCV tooltip on candlestick chart
gemdev111 92dfd50
Merge branch 'main' into perp-chart-tooltip
gemdev111 b34b9d5
Replace 'Vol' with 'Volume' and tweak spacing
gemdev111 e04a758
Merge branch 'main' into perp-chart-tooltip
gemdev111 497c230
Refactor candle tooltip UI and add tests
gemdev111 ce7087c
Merge branch 'main' into perp-chart-tooltip
gemdev111 e66d3fe
Localize candle tooltip and add resources
gemdev111 cfa2847
Merge branch 'main' into perp-chart-tooltip
gemdev111 a49be31
Update CandleTooltipViewModel+TestKit.swift
gemdev111 43b3394
Update CandleTooltipViewModel.swift
gemdev111 8dac718
Merge branch 'main' into perp-chart-tooltip
gemdev111 b618f18
Use Spacing constants and add tooltip divider
gemdev111 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
Features/Perpetuals/Sources/ViewModels/CandleTooltipViewModel.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // Copyright (c). Gem Wallet. All rights reserved. | ||
|
|
||
| import SwiftUI | ||
| import Primitives | ||
| import Formatters | ||
| import Style | ||
| import Components | ||
| import Localization | ||
|
|
||
| public struct CandleTooltipViewModel { | ||
| private static let titleStyle = TextStyle(font: .caption2, color: Colors.secondaryText, fontWeight: .medium) | ||
| private static let subtitleStyle = TextStyle(font: .caption2.monospacedDigit(), color: Colors.black, fontWeight: .semibold) | ||
| private static let volumeFormatter = CurrencyFormatter(type: .abbreviated, currencyCode: Currency.usd.rawValue) | ||
|
|
||
| private let candle: ChartCandleStick | ||
| private let formatter: CurrencyFormatter | ||
|
|
||
| public init(candle: ChartCandleStick, formatter: CurrencyFormatter) { | ||
| self.candle = candle | ||
| self.formatter = formatter | ||
| } | ||
|
|
||
| var openTitle: TextValue { TextValue(text: Localized.Charts.Price.open, style: Self.titleStyle, lineLimit: 1) } | ||
| var openValue: TextValue { TextValue(text: formatter.string(double: candle.open), style: Self.subtitleStyle, lineLimit: 1) } | ||
|
|
||
| var closeTitle: TextValue { TextValue(text: Localized.Charts.Price.close, style: Self.titleStyle, lineLimit: 1) } | ||
| var closeValue: TextValue { TextValue(text: formatter.string(double: candle.close), style: Self.subtitleStyle, lineLimit: 1) } | ||
|
|
||
| var highTitle: TextValue { TextValue(text: Localized.Charts.Price.high, style: Self.titleStyle, lineLimit: 1) } | ||
| var highValue: TextValue { TextValue(text: formatter.string(double: candle.high), style: Self.subtitleStyle, lineLimit: 1) } | ||
|
|
||
| var lowTitle: TextValue { TextValue(text: Localized.Charts.Price.low, style: Self.titleStyle, lineLimit: 1) } | ||
| var lowValue: TextValue { TextValue(text: formatter.string(double: candle.low), style: Self.subtitleStyle, lineLimit: 1) } | ||
|
|
||
| var changeTitle: TextValue { TextValue(text: Localized.Charts.Price.change, style: Self.titleStyle, lineLimit: 1) } | ||
| var changeValue: TextValue { | ||
| let change = PriceChangeCalculator.calculate(.percentage(from: candle.open, to: candle.close)) | ||
| return TextValue(text: CurrencyFormatter.percent.string(change), style: TextStyle(font: .caption2.monospacedDigit(), color: PriceChangeColor.color(for: change), fontWeight: .semibold), lineLimit: 1) | ||
| } | ||
|
|
||
| var volumeTitle: TextValue { TextValue(text: Localized.Perpetual.volume, style: Self.titleStyle, lineLimit: 1) } | ||
| var volumeValue: TextValue { TextValue(text: Self.volumeFormatter.string(candle.volume * candle.close), style: Self.subtitleStyle, lineLimit: 1) } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // Copyright (c). Gem Wallet. All rights reserved. | ||
|
|
||
| import SwiftUI | ||
| import Style | ||
| import Components | ||
|
|
||
| struct CandleTooltipView: View { | ||
| let model: CandleTooltipViewModel | ||
|
|
||
| var body: some View { | ||
| VStack(spacing: Spacing.small) { | ||
| VStack(spacing: Spacing.extraSmall) { | ||
| ListItemView(title: model.openTitle, subtitle: model.openValue) | ||
| ListItemView(title: model.highTitle, subtitle: model.highValue) | ||
| ListItemView(title: model.lowTitle, subtitle: model.lowValue) | ||
| ListItemView(title: model.closeTitle, subtitle: model.closeValue) | ||
| } | ||
| Divider() | ||
| VStack(spacing: Spacing.extraSmall) { | ||
| ListItemView(title: model.changeTitle, subtitle: model.changeValue) | ||
| ListItemView(title: model.volumeTitle, subtitle: model.volumeValue) | ||
| } | ||
| } | ||
| .padding(Spacing.small) | ||
| .background(.thickMaterial) | ||
| .clipShape(RoundedRectangle(cornerRadius: Spacing.small)) | ||
| .overlay( | ||
| RoundedRectangle(cornerRadius: Spacing.small) | ||
| .stroke(Colors.black.opacity(0.08), lineWidth: 1) | ||
| ) | ||
| .shadow(color: .black.opacity(0.12), radius: Spacing.small, y: Spacing.tiny) | ||
| .fixedSize() | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
Features/Perpetuals/TestKit/CandleTooltipViewModel+TestKit.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // Copyright (c). Gem Wallet. All rights reserved. | ||
|
|
||
| import Foundation | ||
| import Primitives | ||
| import Formatters | ||
| @testable import Perpetuals | ||
|
|
||
| public extension CandleTooltipViewModel { | ||
| static func mock( | ||
| candle: ChartCandleStick = .mock(), | ||
| formatter: CurrencyFormatter = CurrencyFormatter(type: .currency, locale: Locale(identifier: "en_US"), currencyCode: "USD") | ||
| ) -> CandleTooltipViewModel { | ||
| CandleTooltipViewModel(candle: candle, formatter: formatter) | ||
| } | ||
| } |
24 changes: 24 additions & 0 deletions
24
Features/Perpetuals/TestKit/ChartCandleStick+TestKit.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // Copyright (c). Gem Wallet. All rights reserved. | ||
|
|
||
| import Foundation | ||
| import Primitives | ||
|
|
||
| public extension ChartCandleStick { | ||
| static func mock( | ||
| date: Date = Date(timeIntervalSince1970: 0), | ||
| open: Double = 100, | ||
| high: Double = 110, | ||
| low: Double = 90, | ||
| close: Double = 105, | ||
| volume: Double = 1000 | ||
| ) -> ChartCandleStick { | ||
| ChartCandleStick( | ||
| date: date, | ||
| open: open, | ||
| high: high, | ||
| low: low, | ||
| close: close, | ||
| volume: volume | ||
| ) | ||
| } | ||
| } |
42 changes: 42 additions & 0 deletions
42
Features/Perpetuals/Tests/CandleTooltipViewModelTests.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // Copyright (c). Gem Wallet. All rights reserved. | ||
|
|
||
| import Testing | ||
| import Primitives | ||
| import PrimitivesTestKit | ||
| import PerpetualsTestKit | ||
| import Formatters | ||
| import Localization | ||
| @testable import Perpetuals | ||
|
|
||
| struct CandleTooltipViewModelTests { | ||
|
|
||
| @Test | ||
| func tooltipContent() { | ||
| let model = CandleTooltipViewModel.mock(candle: .mock(open: 67_715, high: 68_181, low: 67_714, close: 68_087, volume: 500)) | ||
|
|
||
| #expect(model.openTitle.text == Localized.Charts.Price.open) | ||
| #expect(model.openValue.text == "67,715.00") | ||
|
|
||
| #expect(model.highTitle.text == Localized.Charts.Price.high) | ||
| #expect(model.highValue.text == "68,181.00") | ||
|
|
||
| #expect(model.lowTitle.text == Localized.Charts.Price.low) | ||
| #expect(model.lowValue.text == "67,714.00") | ||
|
|
||
| #expect(model.closeTitle.text == Localized.Charts.Price.close) | ||
| #expect(model.closeValue.text == "68,087.00") | ||
|
|
||
| #expect(model.changeTitle.text == Localized.Charts.Price.change) | ||
| #expect(model.changeValue.text == "+0.55%") | ||
|
|
||
| #expect(model.volumeTitle.text == Localized.Perpetual.volume) | ||
| #expect(model.volumeValue.text == "$34.04M") | ||
| } | ||
|
|
||
| @Test | ||
| func changeSign() { | ||
| #expect(CandleTooltipViewModel.mock(candle: .mock(open: 100, close: 105)).changeValue.text == "+5.00%") | ||
| #expect(CandleTooltipViewModel.mock(candle: .mock(open: 100, close: 95)).changeValue.text == "-5.00%") | ||
| #expect(CandleTooltipViewModel.mock(candle: .mock(open: 100, close: 100)).changeValue.text == "+0.00%") | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
The padding applied to the
CandleTooltipViewis unconditional. Specifically,padding(.trailing, Spacing.extraLarge + Spacing.medium)is applied even when the tooltip is aligned to the trailing edge (isRightHalfis false). This will cause the tooltip to be pushed off-screen to the right, leading to a poor user experience. The padding should be conditional based on theisRightHalfflag to ensure the tooltip remains within the visible chart bounds.Consider applying leading padding when
isRightHalfis false (aligned trailing) and trailing padding whenisRightHalfis true (aligned leading).