Skip to content
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
13 changes: 13 additions & 0 deletions Features/Perpetuals/Sources/Types/ChartGridStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c). Gem Wallet. All rights reserved.

import SwiftUI
import Charts
import Style

struct ChartGridStyle {
static let opacity: Double = 0.13
static let lineWidth: CGFloat = 1
static let dash: [CGFloat] = [4, 4]
static let strokeStyle = StrokeStyle(lineWidth: lineWidth, dash: dash)
static let color = Colors.gray.opacity(opacity)
}
18 changes: 9 additions & 9 deletions Features/Perpetuals/Sources/ViewModels/ChartLineViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@ import SwiftUI
import Style
import Localization
import Primitives
import Formatters

struct ChartLineViewModel: Identifiable {
let line: ChartLine
let formatter: CurrencyFormatter

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

var label: String {
switch line.type {
let typeLabel: String = switch line.type {
case .takeProfit: Localized.Charts.takeProfit
case .stopLoss: Localized.Charts.stopLoss
case .entry: Localized.Charts.entry
case .liquidation: Localized.Charts.liquidation
}
let priceText = formatter.string(double: line.price)
return "\(typeLabel) | \(priceText)"
}

var color: Color {
switch line.type {
case .takeProfit: Colors.green
case .stopLoss: Colors.red
case .entry: Colors.blue
case .liquidation: Colors.orange
case .stopLoss: Colors.orange
case .entry: Colors.gray
case .liquidation: Colors.red
}
}

var lineStyle: StrokeStyle {
switch line.type {
case .takeProfit, .stopLoss: StrokeStyle(lineWidth: 1, dash: [6, 4])
case .entry: StrokeStyle(lineWidth: 1.5)
case .liquidation: StrokeStyle(lineWidth: 1, dash: [2, 2])
}
StrokeStyle(lineWidth: 1, dash: [4, 3])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Formatters
import ExplorerService
import Preferences
import BigInt
import GemstonePrimitives

@Observable
@MainActor
Expand Down Expand Up @@ -77,7 +78,10 @@ public final class PerpetualSceneViewModel {
)
}

public var navigationTitle: String { perpetualViewModel.name }
public var navigationTitle: String {
let name = perpetualViewModel.name
return name.isEmpty ? asset.symbol : name
}
public var currency: String { preference.currency }
public var hasOpenPosition: Bool { !positionViewModels.isEmpty }

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

var chartLineModels: [ChartLineViewModel] {
guard let position = positions.first?.position else { return [] }
guard let positionData = positions.first else { return [] }
let position = positionData.position
let prices: [(ChartLineType, Double?)] = [
(.entry, position.entryPrice),
(.takeProfit, position.takeProfit?.price),
(.stopLoss, position.stopLoss?.price),
(.liquidation, position.liquidationPrice)
]
return prices.compactMap { type, price in
price.map { ChartLineViewModel(line: ChartLine(type: type, price: $0)) }
price.map {
ChartLineViewModel(
line: ChartLine(type: type, price: $0),
formatter: CurrencyFormatter(type: .currency, currencyCode: .empty)
)
}
}
}

Expand Down
26 changes: 12 additions & 14 deletions Features/Perpetuals/Sources/Views/CandlestickChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ struct CandlestickChartView: View {
}
.chartXAxis {
AxisMarks(position: .bottom, values: .automatic(desiredCount: 6)) { _ in
AxisGridLine(stroke: StrokeStyle(lineWidth: 1, dash: [4, 4]))
.foregroundStyle(Colors.gray.opacity(0.5))
AxisGridLine(stroke: ChartGridStyle.strokeStyle)
.foregroundStyle(ChartGridStyle.color)
}
}
.chartYAxis {
AxisMarks(position: .trailing, values: .automatic(desiredCount: 5)) { _ in
AxisGridLine(stroke: StrokeStyle(lineWidth: 1, dash: [4, 4]))
.foregroundStyle(Colors.gray.opacity(0.5))
AxisTick(stroke: StrokeStyle(lineWidth: 1))
.foregroundStyle(Colors.gray.opacity(0.5))
AxisGridLine(stroke: ChartGridStyle.strokeStyle)
.foregroundStyle(ChartGridStyle.color)
AxisTick(stroke: StrokeStyle(lineWidth: ChartGridStyle.lineWidth))
.foregroundStyle(ChartGridStyle.color)
AxisValueLabel()
.foregroundStyle(Colors.gray)
.font(.caption2)
Expand Down Expand Up @@ -132,7 +132,7 @@ struct CandlestickChartView: View {
private func linesMarks(_ bounds: ChartBounds) -> some ChartContent {
ForEach(bounds.visibleLines) { line in
RuleMark(y: .value(ChartKey.price, line.price))
.foregroundStyle(line.color.opacity(0.8))
.foregroundStyle(line.color.opacity(0.6))
.lineStyle(line.lineStyle)
}

Expand All @@ -141,23 +141,21 @@ struct CandlestickChartView: View {
.foregroundStyle(.clear)
.annotation(position: .overlay, alignment: .leading, spacing: 0) {
Text(line.label)
.font(.system(size: Spacing.space10, weight: .bold))
.font(.system(size: .space10, weight: .semibold))
.foregroundStyle(Colors.whiteSolid)
.padding(.horizontal, Spacing.extraSmall)
.padding(.vertical, 1)
.padding(.tiny)
.background(line.color)
.clipShape(RoundedRectangle(cornerRadius: Spacing.extraSmall))
.clipShape(RoundedRectangle(cornerRadius: .tiny))
.offset(x: labelXOffset(for: index, in: bounds))
}
}
}

private func labelXOffset(for index: Int, in bounds: ChartBounds) -> CGFloat {
guard index > 0 else { return 0 }
let priceRange = bounds.maxPrice - bounds.minPrice
let threshold = priceRange * 0.06
let space: CGFloat = 75
let threshold = (bounds.maxPrice - bounds.minPrice) * 0.06
let lines = bounds.visibleLines
let space = 115.0
return (1...index).reduce(0.0) { offset, idx in
abs(lines[idx].price - lines[idx - 1].price) < threshold ? offset + space : offset
}
Expand Down
2 changes: 1 addition & 1 deletion Packages/Formatters/Sources/CurrencyFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public struct CurrencyFormatter: Sendable, Hashable {
let formatter = formatter(for: double)
formatter.currencySymbol = ""

let value = formatter.string(from: NSNumber(value: double)) ?? ""
let value = (formatter.string(from: NSNumber(value: double)) ?? "").trimmingCharacters(in: .whitespaces)
return combined(value: value, symbol: symbol)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ final class CurrencyFormatterTests {
let percentFormatterUK = CurrencyFormatter(type: .percent, locale: .UK, currencyCode: Currency.gbp.rawValue)
let percentSignLess = CurrencyFormatter(type: .percentSignLess, locale: .US, currencyCode: Currency.usd.rawValue)

let cryptoFormatter = CurrencyFormatter(type: .currency, locale: .US, currencyCode: "")
let cryptoFormatter = CurrencyFormatter(type: .currency, locale: .US, currencyCode: .empty)
let cryptoFormatterUA = CurrencyFormatter(type: .currency, locale: .RU_UA, currencyCode: .empty)

let abbreviatedFormatterUS = CurrencyFormatter(type: .abbreviated, locale: .US, currencyCode: Currency.usd.rawValue)
let abbreviatedFormatterUK = CurrencyFormatter(type: .abbreviated, locale: .UK, currencyCode: Currency.gbp.rawValue)
Expand Down Expand Up @@ -90,6 +91,10 @@ final class CurrencyFormatterTests {
#expect(cryptoFormatter.string(double: 11.12) == "11.12")
#expect(cryptoFormatter.string(double: 11) == "11.00")
#expect(cryptoFormatter.string(double: 12000123) == "12,000,123.00")

#expect(cryptoFormatterUA.string(double: 0.003011) == cryptoFormatterUA.string(double: 0.003011).trimmingCharacters(in: .whitespaces))
#expect(cryptoFormatterUA.string(double: 92500) == "92 500,00")
#expect(cryptoFormatterUA.string(double: 29.73) == "29,73")
}

@Test
Expand Down
4 changes: 2 additions & 2 deletions Packages/Localization/Sources/Localized.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ public enum Localized {
public static let entry = Localized.tr("Localizable", "charts.entry", fallback: "Entry")
/// 1H
public static let hour = Localized.tr("Localizable", "charts.hour", fallback: "1H")
/// Liquidation
public static let liquidation = Localized.tr("Localizable", "charts.liquidation", fallback: "Liquidation")
/// Liq
public static let liquidation = Localized.tr("Localizable", "charts.liquidation", fallback: "Liq")
/// 1M
public static let month = Localized.tr("Localizable", "charts.month", fallback: "1M")
/// SL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "رسائل إلكترونية مزعجة";
"nft.report.reason.malicious" = "خبيث";
"nft.report.reason.inappropriate" = "محتوى غير لائق";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "স্প্যাম";
"nft.report.reason.malicious" = "দূষিত";
"nft.report.reason.inappropriate" = "অনুপযুক্ত কন্টেন্ট";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Zlomyslný";
"nft.report.reason.inappropriate" = "Nevhodný obsah";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Ondsindet";
"nft.report.reason.inappropriate" = "Upassende indhold";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Böswillig";
"nft.report.reason.inappropriate" = "Unangemessene Inhalte";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Malicious";
"nft.report.reason.inappropriate" = "Inappropriate Content";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Correo basura";
"nft.report.reason.malicious" = "Malicioso";
"nft.report.reason.inappropriate" = "Contenido inapropiado";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "هرزنامه";
"nft.report.reason.malicious" = "مخرب";
"nft.report.reason.inappropriate" = "محتوای نامناسب";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Malisyoso";
"nft.report.reason.inappropriate" = "Hindi Naaangkop na Nilalaman";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Courrier indésirable";
"nft.report.reason.malicious" = "Malveillant";
"nft.report.reason.inappropriate" = "Contenu inapproprié";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Mai mugunta";
"nft.report.reason.inappropriate" = "Abun ciki mara dacewa";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "ספאם";
"nft.report.reason.malicious" = "זְדוֹנִי";
"nft.report.reason.inappropriate" = "תוכן לא הולם";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "अवांछित ईमेल";
"nft.report.reason.malicious" = "दुर्भावनापूर्ण";
"nft.report.reason.inappropriate" = "अनुपयुक्त सामग्री";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Jahat";
"nft.report.reason.inappropriate" = "Konten yang Tidak Pantas";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Malizioso";
"nft.report.reason.inappropriate" = "Contenuto inappropriato";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "スパム";
"nft.report.reason.malicious" = "悪意のある";
"nft.report.reason.inappropriate" = "不適切なコンテンツ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "스팸";
"nft.report.reason.malicious" = "악의 있는";
"nft.report.reason.inappropriate" = "부적절한 콘텐츠";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Berniat jahat";
"nft.report.reason.inappropriate" = "Kandungan Tidak Sesuai";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Kwaadwillend";
"nft.report.reason.inappropriate" = "Ongepaste inhoud";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Złośliwy";
"nft.report.reason.inappropriate" = "Niewłaściwa treść";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
"charts.take_profit" = "TP";
"charts.stop_loss" = "SL";
"charts.entry" = "Entry";
"charts.liquidation" = "Liquidation";
"charts.liquidation" = "Liq";
"nft.report.reason.spam" = "Spam";
"nft.report.reason.malicious" = "Malicioso";
"nft.report.reason.inappropriate" = "Conteúdo impróprio";
Expand Down
Loading
Loading