Skip to content

Commit 7d56d71

Browse files
authored
Perp chart style updates (#1455)
* Update chart line colors and styles for clarity Adjusted the color mapping for stop loss, entry, and liquidation lines in ChartLineViewModel for better visual distinction. Unified the line style to a consistent dashed pattern. Reduced line opacity and refined label padding and corner radius in CandlestickChartView for improved chart readability. * Fixed perp title showed with delay * Refactor chart grid styling into ChartGridStyle Extracted chart grid line and color styling into a new ChartGridStyle struct for reuse and consistency. Updated CandlestickChartView to use ChartGridStyle for axis grid lines and ticks. * Add decimals to ChartLine and improve label formatting ChartLine now includes a decimals property, allowing for more precise price formatting. ChartLineViewModel uses PerpetualFormatter to display price labels with correct decimal places. CandlestickChartView label styling and spacing have been adjusted for better appearance. * Refactor chart line formatting and remove decimals Replaced PerpetualFormatter with CurrencyFormatter in ChartLineViewModel and PerpetualSceneViewModel. Removed decimals property from ChartLine struct and its usage. Updated CurrencyFormatter to trim whitespace from formatted strings and added related tests. Simplified label padding in CandlestickChartView. * Abbreviate 'Liquidation' to 'Liq' in chart labels Replaces the 'Liquidation' label with 'Liq' for the 'charts.liquidation' key across all supported localizations and updates the fallback value in Localized.swift. This change ensures consistency and brevity in chart terminology.
1 parent 68484d7 commit 7d56d71

38 files changed

+87
-61
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c). Gem Wallet. All rights reserved.
2+
3+
import SwiftUI
4+
import Charts
5+
import Style
6+
7+
struct ChartGridStyle {
8+
static let opacity: Double = 0.13
9+
static let lineWidth: CGFloat = 1
10+
static let dash: [CGFloat] = [4, 4]
11+
static let strokeStyle = StrokeStyle(lineWidth: lineWidth, dash: dash)
12+
static let color = Colors.gray.opacity(opacity)
13+
}

Features/Perpetuals/Sources/ViewModels/ChartLineViewModel.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,36 @@ import SwiftUI
44
import Style
55
import Localization
66
import Primitives
7+
import Formatters
78

89
struct ChartLineViewModel: Identifiable {
910
let line: ChartLine
11+
let formatter: CurrencyFormatter
1012

1113
var id: String { "\(line.type)_\(line.price)" }
1214
var price: Double { line.price }
1315

1416
var label: String {
15-
switch line.type {
17+
let typeLabel: String = switch line.type {
1618
case .takeProfit: Localized.Charts.takeProfit
1719
case .stopLoss: Localized.Charts.stopLoss
1820
case .entry: Localized.Charts.entry
1921
case .liquidation: Localized.Charts.liquidation
2022
}
23+
let priceText = formatter.string(double: line.price)
24+
return "\(typeLabel) | \(priceText)"
2125
}
2226

2327
var color: Color {
2428
switch line.type {
2529
case .takeProfit: Colors.green
26-
case .stopLoss: Colors.red
27-
case .entry: Colors.blue
28-
case .liquidation: Colors.orange
30+
case .stopLoss: Colors.orange
31+
case .entry: Colors.gray
32+
case .liquidation: Colors.red
2933
}
3034
}
3135

3236
var lineStyle: StrokeStyle {
33-
switch line.type {
34-
case .takeProfit, .stopLoss: StrokeStyle(lineWidth: 1, dash: [6, 4])
35-
case .entry: StrokeStyle(lineWidth: 1.5)
36-
case .liquidation: StrokeStyle(lineWidth: 1, dash: [2, 2])
37-
}
37+
StrokeStyle(lineWidth: 1, dash: [4, 3])
3838
}
3939
}

Features/Perpetuals/Sources/ViewModels/PerpetualSceneViewModel.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Formatters
1313
import ExplorerService
1414
import Preferences
1515
import BigInt
16+
import GemstonePrimitives
1617

1718
@Observable
1819
@MainActor
@@ -77,7 +78,10 @@ public final class PerpetualSceneViewModel {
7778
)
7879
}
7980

80-
public var navigationTitle: String { perpetualViewModel.name }
81+
public var navigationTitle: String {
82+
let name = perpetualViewModel.name
83+
return name.isEmpty ? asset.symbol : name
84+
}
8185
public var currency: String { preference.currency }
8286
public var hasOpenPosition: Bool { !positionViewModels.isEmpty }
8387

@@ -96,15 +100,21 @@ public final class PerpetualSceneViewModel {
96100
public var positionViewModels: [PerpetualPositionViewModel] { positions.map { PerpetualPositionViewModel($0) } }
97101

98102
var chartLineModels: [ChartLineViewModel] {
99-
guard let position = positions.first?.position else { return [] }
103+
guard let positionData = positions.first else { return [] }
104+
let position = positionData.position
100105
let prices: [(ChartLineType, Double?)] = [
101106
(.entry, position.entryPrice),
102107
(.takeProfit, position.takeProfit?.price),
103108
(.stopLoss, position.stopLoss?.price),
104109
(.liquidation, position.liquidationPrice)
105110
]
106111
return prices.compactMap { type, price in
107-
price.map { ChartLineViewModel(line: ChartLine(type: type, price: $0)) }
112+
price.map {
113+
ChartLineViewModel(
114+
line: ChartLine(type: type, price: $0),
115+
formatter: CurrencyFormatter(type: .currency, currencyCode: .empty)
116+
)
117+
}
108118
}
109119
}
110120

Features/Perpetuals/Sources/Views/CandlestickChartView.swift

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,16 @@ struct CandlestickChartView: View {
8888
}
8989
.chartXAxis {
9090
AxisMarks(position: .bottom, values: .automatic(desiredCount: 6)) { _ in
91-
AxisGridLine(stroke: StrokeStyle(lineWidth: 1, dash: [4, 4]))
92-
.foregroundStyle(Colors.gray.opacity(0.5))
91+
AxisGridLine(stroke: ChartGridStyle.strokeStyle)
92+
.foregroundStyle(ChartGridStyle.color)
9393
}
9494
}
9595
.chartYAxis {
9696
AxisMarks(position: .trailing, values: .automatic(desiredCount: 5)) { _ in
97-
AxisGridLine(stroke: StrokeStyle(lineWidth: 1, dash: [4, 4]))
98-
.foregroundStyle(Colors.gray.opacity(0.5))
99-
AxisTick(stroke: StrokeStyle(lineWidth: 1))
100-
.foregroundStyle(Colors.gray.opacity(0.5))
97+
AxisGridLine(stroke: ChartGridStyle.strokeStyle)
98+
.foregroundStyle(ChartGridStyle.color)
99+
AxisTick(stroke: StrokeStyle(lineWidth: ChartGridStyle.lineWidth))
100+
.foregroundStyle(ChartGridStyle.color)
101101
AxisValueLabel()
102102
.foregroundStyle(Colors.gray)
103103
.font(.caption2)
@@ -132,7 +132,7 @@ struct CandlestickChartView: View {
132132
private func linesMarks(_ bounds: ChartBounds) -> some ChartContent {
133133
ForEach(bounds.visibleLines) { line in
134134
RuleMark(y: .value(ChartKey.price, line.price))
135-
.foregroundStyle(line.color.opacity(0.8))
135+
.foregroundStyle(line.color.opacity(0.6))
136136
.lineStyle(line.lineStyle)
137137
}
138138

@@ -141,23 +141,21 @@ struct CandlestickChartView: View {
141141
.foregroundStyle(.clear)
142142
.annotation(position: .overlay, alignment: .leading, spacing: 0) {
143143
Text(line.label)
144-
.font(.system(size: Spacing.space10, weight: .bold))
144+
.font(.system(size: .space10, weight: .semibold))
145145
.foregroundStyle(Colors.whiteSolid)
146-
.padding(.horizontal, Spacing.extraSmall)
147-
.padding(.vertical, 1)
146+
.padding(.tiny)
148147
.background(line.color)
149-
.clipShape(RoundedRectangle(cornerRadius: Spacing.extraSmall))
148+
.clipShape(RoundedRectangle(cornerRadius: .tiny))
150149
.offset(x: labelXOffset(for: index, in: bounds))
151150
}
152151
}
153152
}
154153

155154
private func labelXOffset(for index: Int, in bounds: ChartBounds) -> CGFloat {
156155
guard index > 0 else { return 0 }
157-
let priceRange = bounds.maxPrice - bounds.minPrice
158-
let threshold = priceRange * 0.06
159-
let space: CGFloat = 75
156+
let threshold = (bounds.maxPrice - bounds.minPrice) * 0.06
160157
let lines = bounds.visibleLines
158+
let space = 115.0
161159
return (1...index).reduce(0.0) { offset, idx in
162160
abs(lines[idx].price - lines[idx - 1].price) < threshold ? offset + space : offset
163161
}

Packages/Formatters/Sources/CurrencyFormatter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public struct CurrencyFormatter: Sendable, Hashable {
133133
let formatter = formatter(for: double)
134134
formatter.currencySymbol = ""
135135

136-
let value = formatter.string(from: NSNumber(value: double)) ?? ""
136+
let value = (formatter.string(from: NSNumber(value: double)) ?? "").trimmingCharacters(in: .whitespaces)
137137
return combined(value: value, symbol: symbol)
138138
}
139139

Packages/Formatters/Tests/FormattersTests/CurrencyFormatterTests.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ final class CurrencyFormatterTests {
1414
let percentFormatterUK = CurrencyFormatter(type: .percent, locale: .UK, currencyCode: Currency.gbp.rawValue)
1515
let percentSignLess = CurrencyFormatter(type: .percentSignLess, locale: .US, currencyCode: Currency.usd.rawValue)
1616

17-
let cryptoFormatter = CurrencyFormatter(type: .currency, locale: .US, currencyCode: "")
17+
let cryptoFormatter = CurrencyFormatter(type: .currency, locale: .US, currencyCode: .empty)
18+
let cryptoFormatterUA = CurrencyFormatter(type: .currency, locale: .RU_UA, currencyCode: .empty)
1819

1920
let abbreviatedFormatterUS = CurrencyFormatter(type: .abbreviated, locale: .US, currencyCode: Currency.usd.rawValue)
2021
let abbreviatedFormatterUK = CurrencyFormatter(type: .abbreviated, locale: .UK, currencyCode: Currency.gbp.rawValue)
@@ -90,6 +91,10 @@ final class CurrencyFormatterTests {
9091
#expect(cryptoFormatter.string(double: 11.12) == "11.12")
9192
#expect(cryptoFormatter.string(double: 11) == "11.00")
9293
#expect(cryptoFormatter.string(double: 12000123) == "12,000,123.00")
94+
95+
#expect(cryptoFormatterUA.string(double: 0.003011) == cryptoFormatterUA.string(double: 0.003011).trimmingCharacters(in: .whitespaces))
96+
#expect(cryptoFormatterUA.string(double: 92500) == "92 500,00")
97+
#expect(cryptoFormatterUA.string(double: 29.73) == "29,73")
9398
}
9499

95100
@Test

Packages/Localization/Sources/Localized.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ public enum Localized {
211211
public static let entry = Localized.tr("Localizable", "charts.entry", fallback: "Entry")
212212
/// 1H
213213
public static let hour = Localized.tr("Localizable", "charts.hour", fallback: "1H")
214-
/// Liquidation
215-
public static let liquidation = Localized.tr("Localizable", "charts.liquidation", fallback: "Liquidation")
214+
/// Liq
215+
public static let liquidation = Localized.tr("Localizable", "charts.liquidation", fallback: "Liq")
216216
/// 1M
217217
public static let month = Localized.tr("Localizable", "charts.month", fallback: "1M")
218218
/// SL

Packages/Localization/Sources/Resources/ar.lproj/Localizable.strings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@
518518
"charts.take_profit" = "TP";
519519
"charts.stop_loss" = "SL";
520520
"charts.entry" = "Entry";
521-
"charts.liquidation" = "Liquidation";
521+
"charts.liquidation" = "Liq";
522522
"nft.report.reason.spam" = "رسائل إلكترونية مزعجة";
523523
"nft.report.reason.malicious" = "خبيث";
524524
"nft.report.reason.inappropriate" = "محتوى غير لائق";

Packages/Localization/Sources/Resources/bn.lproj/Localizable.strings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@
518518
"charts.take_profit" = "TP";
519519
"charts.stop_loss" = "SL";
520520
"charts.entry" = "Entry";
521-
"charts.liquidation" = "Liquidation";
521+
"charts.liquidation" = "Liq";
522522
"nft.report.reason.spam" = "স্প্যাম";
523523
"nft.report.reason.malicious" = "দূষিত";
524524
"nft.report.reason.inappropriate" = "অনুপযুক্ত কন্টেন্ট";

Packages/Localization/Sources/Resources/cs.lproj/Localizable.strings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@
518518
"charts.take_profit" = "TP";
519519
"charts.stop_loss" = "SL";
520520
"charts.entry" = "Entry";
521-
"charts.liquidation" = "Liquidation";
521+
"charts.liquidation" = "Liq";
522522
"nft.report.reason.spam" = "Spam";
523523
"nft.report.reason.malicious" = "Zlomyslný";
524524
"nft.report.reason.inappropriate" = "Nevhodný obsah";

0 commit comments

Comments
 (0)