diff --git a/BlackjackSim/BlackjackSim.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/BlackjackSim/BlackjackSim.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..919434a62
--- /dev/null
+++ b/BlackjackSim/BlackjackSim.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/PlatformUI/PlatformUI.xcodeproj/project.pbxproj b/PlatformUI/PlatformUI.xcodeproj/project.pbxproj
index bea8ca59c..56a42e61b 100644
--- a/PlatformUI/PlatformUI.xcodeproj/project.pbxproj
+++ b/PlatformUI/PlatformUI.xcodeproj/project.pbxproj
@@ -43,6 +43,7 @@
27044F882BBB2ADF004C750D /* Text+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27044F872BBB2ADF004C750D /* Text+Ext.swift */; };
274C47EA2C0FC6CF000212C3 /* EdgeInsets+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C47E92C0FC6CF000212C3 /* EdgeInsets+Ext.swift */; };
276581F82C139F36009E072A /* SingleAxisGeometryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276581F72C139F36009E072A /* SingleAxisGeometryReader.swift */; };
+ 27673BBE2C8FD24400689E3F /* AttributedString+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27673BBD2C8FD24400689E3F /* AttributedString+Ext.swift */; };
277E7AC62BBF3BE8009F95DE /* InlineAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E7AC52BBF3BE8009F95DE /* InlineAlert.swift */; };
27E6A7322AB8D5F600026CB5 /* SwipeActionsViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E6A7312AB8D5F600026CB5 /* SwipeActionsViewModifier.swift */; };
6488BBDC296F6AEA0096502F /* TabItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6488BBDB296F6AEA0096502F /* TabItemViewModel.swift */; };
@@ -139,6 +140,7 @@
27044F872BBB2ADF004C750D /* Text+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Text+Ext.swift"; sourceTree = ""; };
274C47E92C0FC6CF000212C3 /* EdgeInsets+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EdgeInsets+Ext.swift"; sourceTree = ""; };
276581F72C139F36009E072A /* SingleAxisGeometryReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleAxisGeometryReader.swift; sourceTree = ""; };
+ 27673BBD2C8FD24400689E3F /* AttributedString+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Ext.swift"; sourceTree = ""; };
277E7AC52BBF3BE8009F95DE /* InlineAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineAlert.swift; sourceTree = ""; };
27E6A7312AB8D5F600026CB5 /* SwipeActionsViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeActionsViewModifier.swift; sourceTree = ""; };
366BD14FE1ED4F2AF21D924E /* Pods-iOS-PlatformUI.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOS-PlatformUI.release.xcconfig"; path = "Target Support Files/Pods-iOS-PlatformUI/Pods-iOS-PlatformUI.release.xcconfig"; sourceTree = ""; };
@@ -381,6 +383,7 @@
children = (
27044F872BBB2ADF004C750D /* Text+Ext.swift */,
274C47E92C0FC6CF000212C3 /* EdgeInsets+Ext.swift */,
+ 27673BBD2C8FD24400689E3F /* AttributedString+Ext.swift */,
);
path = Extensions;
sourceTree = "";
@@ -664,6 +667,7 @@
02DE88F828A2DB9C00728FF3 /* PlatformUIBundleClass.swift in Sources */,
02F16FD928B47E2D0085DC58 /* PlatformTableViewCell.swift in Sources */,
024B794528B6A6A200F7C386 /* PlatformIcon.swift in Sources */,
+ 27673BBE2C8FD24400689E3F /* AttributedString+Ext.swift in Sources */,
0230377828C15C0E00412B72 /* PlatformViewModel.swift in Sources */,
276581F82C139F36009E072A /* SingleAxisGeometryReader.swift in Sources */,
0278DD0E2A7C7A1400FE6ABE /* PlatformViewModel+Ext.swift in Sources */,
diff --git a/PlatformUI/PlatformUI/Components/Buttons/PlatformButton.swift b/PlatformUI/PlatformUI/Components/Buttons/PlatformButton.swift
index a0b22a407..940b2597c 100644
--- a/PlatformUI/PlatformUI/Components/Buttons/PlatformButton.swift
+++ b/PlatformUI/PlatformUI/Components/Buttons/PlatformButton.swift
@@ -67,6 +67,9 @@ public class PlatformButtonViewModel: PlatformVie
switch self.state {
case .primary:
button
+ // adding invisible border ensures different states have equal heights/widths,
+ // otherwise, no border buttons are slightly shorter than border buttons
+ .border(borderWidth: borderWidth, cornerRadius: cornerRadius, borderColor: ThemeColor.SemanticColor.transparent.color)
case .destructive:
button
.border(borderWidth: borderWidth, cornerRadius: cornerRadius, borderColor: ThemeColor.SemanticColor.colorFadedRed.color)
diff --git a/PlatformUI/PlatformUI/Components/Extensions/AttributedString+Ext.swift b/PlatformUI/PlatformUI/Components/Extensions/AttributedString+Ext.swift
new file mode 100644
index 000000000..230217111
--- /dev/null
+++ b/PlatformUI/PlatformUI/Components/Extensions/AttributedString+Ext.swift
@@ -0,0 +1,42 @@
+//
+// AttributedString+Ext.swift
+// PlatformUI
+//
+// Created by Michael Maguire on 9/9/24.
+//
+
+import Utilities
+
+public extension AttributedString {
+ init(text: String,
+ // localizerPathKeys mapped to the action to be performed when tapped
+ hyperlinks: [String: String],
+ foreground: ThemeColor.SemanticColor = .textPrimary
+ ) {
+ var text = text
+ for (key, url) in hyperlinks {
+ let hyperlinkText = DataLocalizer.localize(path: key)
+ // markdown supported as of iOS 15!
+ text = text.replacingOccurrences(of: hyperlinkText, with: "[\(hyperlinkText)](\(url))")
+ }
+ var attributedString = (try? AttributedString(markdown: text)) ?? AttributedString(text)
+ .themeColor(foreground: foreground)
+ for run in attributedString.runs {
+ if let _ = run.link {
+ attributedString[run.range].underlineStyle = .single
+ attributedString[run.range].foregroundColor = foreground.color // Change to any desired color
+ }
+ }
+ self = attributedString
+ }
+
+ init(localizerPathKey: String,
+ params: [String: String]? = nil,
+ // localizerPathKeys mapped to the action to be performed when tapped
+ hyperlinks: [String: String],
+ foreground: ThemeColor.SemanticColor = .textPrimary
+ ) {
+ var localizedText = DataLocalizer.localize(path: localizerPathKey, params: params)
+ self.init(text: localizedText, hyperlinks: hyperlinks, foreground: foreground)
+ }
+}
diff --git a/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift b/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift
index e0541b770..1e493271c 100644
--- a/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift
+++ b/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift
@@ -24,7 +24,6 @@ public class InlineAlertViewModel: PlatformViewModel {
private var title: AnyView? {
guard let titleText = config.title else { return nil }
return Text(titleText)
- .themeColor(foreground: .textPrimary)
.themeFont(fontType: .plus, fontSize: .medium)
.wrappedInAnyView()
}
@@ -32,7 +31,6 @@ public class InlineAlertViewModel: PlatformViewModel {
private var body: AnyView? {
guard let bodyText = config.body else { return nil }
return Text(bodyText)
- .themeColor(foreground: .textPrimary)
.themeFont(fontType: .base, fontSize: .small)
.wrappedInAnyView()
}
@@ -66,11 +64,21 @@ public class InlineAlertViewModel: PlatformViewModel {
public extension InlineAlertViewModel {
struct Config {
- public var title: String?
- public var body: String?
+ public var title: AttributedString? = nil
+ public var body: AttributedString? = nil
public var level: Level
public init(title: String?, body: String?, level: Level) {
+ if let title = title {
+ self.title = AttributedString(title)
+ }
+ if let body = body {
+ self.body = AttributedString(body)
+ }
+ self.level = level
+ }
+
+ public init(title: AttributedString?, body: AttributedString?, level: Level) {
self.title = title
self.body = body
self.level = level
diff --git a/dydx/dydx.xcworkspace/xcshareddata/swiftpm/Package.resolved b/dydx/dydx.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 1601797ea..30adbe9ee 100644
--- a/dydx/dydx.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/dydx/dydx.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -1,5 +1,5 @@
{
- "originHash" : "975d00e29efb8d2ca017c5e61df90418ac01f7d7143e85a3f9ddb4eb982154e4",
+ "originHash" : "3cd7346cace16cf660f9c22302c5ca6770335c67902f3fd155011356a9d43929",
"pins" : [
{
"identity" : "bigint",
@@ -37,15 +37,6 @@
"version" : "2.0.2"
}
},
- {
- "identity" : "keyboardobserving",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/nickffox/KeyboardObserving",
- "state" : {
- "branch" : "master",
- "revision" : "48134b5460435cc9d048223ad7560ee2e40f3d4a"
- }
- },
{
"identity" : "percy-xcui-swift",
"kind" : "remoteSourceControl",
@@ -199,15 +190,6 @@
"version" : "9.0.9"
}
},
- {
- "identity" : "swiftui-introspect",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/siteline/SwiftUI-Introspect.git",
- "state" : {
- "revision" : "121c146fe591b1320238d054ae35c81ffa45f45a",
- "version" : "0.12.0"
- }
- },
{
"identity" : "wallet-mobile-sdk",
"kind" : "remoteSourceControl",
diff --git a/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj b/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj
index f7c7eb298..a763ff5e1 100644
--- a/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj
+++ b/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj
@@ -139,6 +139,7 @@
2741B25C2C06A0E100693B17 /* dydxAnalytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2741B2572C06A0CC00693B17 /* dydxAnalytics.framework */; };
2741E3702A68787A000FA190 /* settings_direction_color_preference.json in Resources */ = {isa = PBXBuildFile; fileRef = 2741E3632A68787A000FA190 /* settings_direction_color_preference.json */; };
2741E3732A689740000FA190 /* dydxDirectionColorPreferenceViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2741E3722A689740000FA190 /* dydxDirectionColorPreferenceViewBuilder.swift */; };
+ 27457F5B2C8AC8B700873640 /* dydxVaultDepositWithdrawConfirmationViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27457F5A2C8AC8B700873640 /* dydxVaultDepositWithdrawConfirmationViewBuilder.swift */; };
2749F8FF2B853B8700D6BA16 /* dydxRewardsLaunchIncentivesPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2749F8FE2B853B8700D6BA16 /* dydxRewardsLaunchIncentivesPresenter.swift */; };
2751D6432C59614C00B36F95 /* tabs_v4_vault.json in Resources */ = {isa = PBXBuildFile; fileRef = 2751D6352C59614C00B36F95 /* tabs_v4_vault.json */; };
2751D6462C59643800B36F95 /* dydxVaultViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2751D6452C59643800B36F95 /* dydxVaultViewBuilder.swift */; };
@@ -529,6 +530,7 @@
2741E3632A68787A000FA190 /* settings_direction_color_preference.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = settings_direction_color_preference.json; sourceTree = ""; };
2741E3722A689740000FA190 /* dydxDirectionColorPreferenceViewBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = dydxDirectionColorPreferenceViewBuilder.swift; sourceTree = ""; };
2742C05D2BF6898500E13C09 /* dydxAnalytics.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = dydxAnalytics.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 27457F5A2C8AC8B700873640 /* dydxVaultDepositWithdrawConfirmationViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxVaultDepositWithdrawConfirmationViewBuilder.swift; sourceTree = ""; };
2749F8FE2B853B8700D6BA16 /* dydxRewardsLaunchIncentivesPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxRewardsLaunchIncentivesPresenter.swift; sourceTree = ""; };
2751D6352C59614C00B36F95 /* tabs_v4_vault.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tabs_v4_vault.json; sourceTree = ""; };
2751D6452C59643800B36F95 /* dydxVaultViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxVaultViewBuilder.swift; sourceTree = ""; };
@@ -1422,11 +1424,19 @@
path = DirectionColorPreference;
sourceTree = "";
};
+ 27457F4C2C8AC89800873640 /* Landing */ = {
+ isa = PBXGroup;
+ children = (
+ 2751D6452C59643800B36F95 /* dydxVaultViewBuilder.swift */,
+ );
+ path = Landing;
+ sourceTree = "";
+ };
2751D6442C59642000B36F95 /* Vault */ = {
isa = PBXGroup;
children = (
+ 27457F4C2C8AC89800873640 /* Landing */,
27823D022C77E30F009BCD51 /* DepositsAndWithdrawals */,
- 2751D6452C59643800B36F95 /* dydxVaultViewBuilder.swift */,
);
path = Vault;
sourceTree = "";
@@ -1455,6 +1465,7 @@
isa = PBXGroup;
children = (
27823CF32C77E21A009BCD51 /* dydxVaultDepositWithdrawViewBuilder.swift */,
+ 27457F5A2C8AC8B700873640 /* dydxVaultDepositWithdrawConfirmationViewBuilder.swift */,
);
path = DepositsAndWithdrawals;
sourceTree = "";
@@ -2032,6 +2043,7 @@
02FAFA5C29D4E08E001A0903 /* dydxDebugViewBuilder.swift in Sources */,
02F543B22C17ABBB000924E4 /* dydxGasTokenViewBuilder.swift in Sources */,
02F700FE29EA0FD9004DEB5E /* dydxReceiptPresenter.swift in Sources */,
+ 27457F5B2C8AC8B700873640 /* dydxVaultDepositWithdrawConfirmationViewBuilder.swift in Sources */,
027E1EF829CA27CD0098666F /* dydxSettingsLandingViewBuilder.swift in Sources */,
020DBF1E29E092C00068AAA6 /* dydxTransferDepositViewPresenter.swift in Sources */,
277987512BA33F15006DC5CD /* dydxSelectedMarketStore.swift in Sources */,
diff --git a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json
index e961650ab..215199aff 100644
--- a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json
+++ b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json
@@ -361,6 +361,14 @@
"destination":"dydxPresenters.dydxVaultDepositWithdrawViewBuilder",
"presentation":"prompt"
},
+ "/vault/deposit_confirm":{
+ "destination":"dydxPresenters.dydxVaultDepositWithdrawConfirmationViewBuilder",
+ "presentation":"push"
+ },
+ "/vault/withdraw_confirm":{
+ "destination":"dydxPresenters.dydxVaultDepositWithdrawConfirmationViewBuilder",
+ "presentation":"push"
+ },
"/wallets":{
"destination":"dydxPresenters.Wallets2ViewBuilder",
"presentation":"half"
diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawConfirmationViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawConfirmationViewBuilder.swift
new file mode 100644
index 000000000..e93f52b9b
--- /dev/null
+++ b/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawConfirmationViewBuilder.swift
@@ -0,0 +1,105 @@
+//
+// dydxVaultDepositWithdrawConfirmationViewBuilder.swift
+// dydxPresenters
+//
+// Created by Michael Maguire on 9/6/24.
+//
+
+import Utilities
+import dydxViews
+import PlatformParticles
+import RoutingKit
+import ParticlesKit
+import PlatformUI
+import dydxStateManager
+import FloatingPanel
+import PlatformRouting
+import dydxFormatter
+
+public class dydxVaultDepositWithdrawConfirmationViewBuilder: NSObject, ObjectBuilderProtocol {
+ public func build() -> T? {
+ let presenter = dydxVaultDepositWithdrawConfirmationViewPresenter()
+ let view = presenter.viewModel?.createView() ?? PlatformViewModel().createView()
+ let viewController = dydxVaultDepositWithdrawConfirmationViewController(presenter: presenter, view: view, configuration: .default)
+ return viewController as? T
+ }
+}
+
+private class dydxVaultDepositWithdrawConfirmationViewController: HostingViewController {
+
+ override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool {
+ let presenter = presenter as? dydxVaultDepositWithdrawConfirmationViewPresenterProtocol
+ if request?.path == "/vault/deposit_confirm" {
+ presenter?.transferType = .deposit
+ return true
+ } else if request?.path == "/vault/withdraw_confirm" {
+ presenter?.transferType = .withdraw
+ return true
+ } else {
+ return false
+ }
+ }
+}
+
+private protocol dydxVaultDepositWithdrawConfirmationViewPresenterProtocol: HostedViewPresenterProtocol {
+ var viewModel: dydxVaultDepositWithdrawConfirmationViewModel? { get }
+ var transferType: VaultTransferType { get set }
+}
+
+private class dydxVaultDepositWithdrawConfirmationViewPresenter: HostedViewPresenter, dydxVaultDepositWithdrawConfirmationViewPresenterProtocol {
+ var transferType: VaultTransferType = .deposit {
+ didSet {
+ viewModel?.transferType = transferType
+ }
+ }
+
+ override init() {
+
+ super.init()
+
+ let viewModel = dydxVaultDepositWithdrawConfirmationViewModel(transferType: transferType)
+
+ viewModel.cancelAction = {
+ Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil)
+ }
+
+ //TODO: replace
+ viewModel.elevatedSlippageAmount = 4.20
+ viewModel.requiresAcknowledgeHighSlippage = true
+
+ self.viewModel = viewModel
+ }
+
+ override func start() {
+ super.start()
+
+ //TODO: replace with real hooks from abacus
+ update()
+ }
+
+ //TODO: replace with real data from abacus
+ func update() {
+ let crossFreeCollateralReceiptItem = dydxReceiptChangeItemView(title: DataLocalizer.localize(path: "APP.GENERAL.CROSS_FREE_COLLATERAL"),
+ value: AmountChangeModel(before: AmountTextModel(amount: 30.01),
+ after: AmountTextModel(amount: 30.02)))
+ let yourVaultBalanceReceiptItem = dydxReceiptChangeItemView(title: DataLocalizer.localize(path: "APP.VAULTS.YOUR_VAULT_BALANCE"),
+ value: AmountChangeModel(before: AmountTextModel(amount: 30.01),
+ after: AmountTextModel(amount: 30.02)))
+ let estSlippageReceiptItem = dydxReceiptChangeItemView(title: DataLocalizer.localize(path: "APP.VAULTS.EST_SLIPPAGE"),
+ value: AmountChangeModel(before: AmountTextModel(amount: 30.01),
+ after: AmountTextModel(amount: 30.02)))
+ let expectedAmountReceivedItem = dydxReceiptChangeItemView(title: DataLocalizer.localize(path: "APP.WITHDRAW_MODAL.EXPECTED_AMOUNT_RECEIVED"),
+ value: AmountChangeModel(before: AmountTextModel(amount: 30.01),
+ after: AmountTextModel(amount: 30.02)))
+ let crossMarginUsageItem = dydxReceiptChangeItemView(title: DataLocalizer.localize(path: "APP.GENERAL.CROSS_MARGIN_USAGE"),
+ value: AmountChangeModel(before: AmountTextModel(amount: 30.01),
+ after: AmountTextModel(amount: 30.02)))
+
+ switch transferType {
+ case .deposit:
+ viewModel?.receiptItems = [crossFreeCollateralReceiptItem, crossMarginUsageItem, yourVaultBalanceReceiptItem]
+ case .withdraw:
+ viewModel?.receiptItems = [crossFreeCollateralReceiptItem, yourVaultBalanceReceiptItem, estSlippageReceiptItem, expectedAmountReceivedItem]
+ }
+ }
+}
diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawViewBuilder.swift
index 54b58b084..233410bd6 100644
--- a/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawViewBuilder.swift
+++ b/dydx/dydxPresenters/dydxPresenters/_v4/Vault/DepositsAndWithdrawals/dydxVaultDepositWithdrawViewBuilder.swift
@@ -50,10 +50,19 @@ private class dydxVaultDepositWithdrawViewPresenter: HostedViewPresenter Void)?
+ public var cancelAction: (() -> Void)?
+ public var elevatedSlippageAmount: Double?
+ public var receiptItems: [dydxReceiptChangeItemView]?
+
+ @Published public var transferType: VaultTransferType
+ @Published public var requiresAcknowledgeHighSlippage: Bool = false
+
+ @Published fileprivate var amount: Double?
+ @Published fileprivate var hasAcknowledgedHighSlippage: Bool = false
+
+ public init(transferType: VaultTransferType) {
+ self.transferType = transferType
+
+
+ }
+
+ public override func createView(parentStyle: ThemeStyle = ThemeStyle.defaultStyle, styleKey: String? = nil) -> PlatformView {
+ PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] style in
+ guard let self = self else { return AnyView(PlatformView.nilView) }
+ return VaultDepositWithdrawConfirmationView(viewModel: self)
+ .wrappedInAnyView()
+ }
+ }
+}
+
+fileprivate struct VaultDepositWithdrawConfirmationView: View {
+ @ObservedObject var viewModel: dydxVaultDepositWithdrawConfirmationViewModel
+
+ var options = VaultTransferType.allCases
+
+ var body: some View {
+
+ VStack(spacing: 24) {
+ titleRow
+ transferVisualization
+ Spacer()
+ receipts
+ withdrawalSlippageInlineAlert
+ checkboxRow
+ submitButton
+ }
+ .padding(.horizontal, 16)
+ .padding(.top, 48)
+ .padding(.bottom, self.safeAreaInsets?.bottom)
+ .makeSheet()
+ .frame(maxWidth: .infinity)
+ .themeColor(background: .layer3)
+ .ignoresSafeArea(edges: [.bottom])
+ }
+
+ private var titleRow: some View {
+ HStack(spacing: 16) {
+ backButton
+ titleText
+ Spacer()
+ }
+ .padding(.horizontal, 8)
+ }
+
+ private var titleText: some View {
+ Text(viewModel.transferType.confirmTransferText)
+ .themeColor(foreground: .textPrimary)
+ .themeFont(fontType: .plus, fontSize: .largest)
+ }
+
+ private var backButton: some View {
+ ChevronBackButtonModel(onBackButtonTap: viewModel.cancelAction ?? {})
+ .createView()
+ }
+
+ private var transferVisualization: some View {
+ return HStack(alignment: .center, spacing: 16) {
+ generationTransferStep(title: viewModel.transferType.transferOriginTitleText, thumbnail: viewModel.transferType.transferOriginImage, subtitle: "placeholder")
+ Image(systemName: "chevron.forward.dotted.chevron.forward")
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: 16, height: 16)
+ .themeColor(foreground: .textTertiary)
+ generationTransferStep(title: viewModel.transferType.transferDestinationTitleText, thumbnail: viewModel.transferType.transferDestinationImage, subtitle: viewModel.transferType.transferDestinationSubtitleText)
+ }
+ .fixedSize(horizontal: false, vertical: true)
+
+ }
+
+
+ private func generationTransferStep(title: String, thumbnail: Image?, subtitle: String) -> some View {
+ VStack(spacing: 8) {
+ Text(title)
+ .themeColor(foreground: .textTertiary)
+ .themeFont(fontType: .base, fontSize: .small)
+ .lineLimit(1)
+ .minimumScaleFactor(0.5)
+ VStack(spacing: 8) {
+ if let thumbnail {
+ thumbnail
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: 32, height: 32)
+ } else {
+ let thumbnailText = subtitle.prefix(1)
+ Text(thumbnailText)
+ .frame(width: 32, height: 32)
+ .themeColor(foreground: .textTertiary)
+ .themeColor(background: .layer1)
+ .clipShape(.circle)
+ }
+ Text(subtitle)
+ .themeColor(foreground: .textSecondary)
+ .themeFont(fontType: .base, fontSize: .medium)
+ .lineLimit(1)
+ .minimumScaleFactor(0.5)
+ }
+ .centerAligned()
+ .padding(.all, 16)
+ .themeColor(background: .layer2)
+ .clipShape(.rect(cornerRadius: 10))
+ }
+ }
+
+ @ViewBuilder
+ private var receipts: some View {
+ if viewModel.receiptItems?.count ?? 0 > 0 {
+ VStack(spacing: 8) {
+ ForEach(viewModel.receiptItems ?? [], id: \.id) { $0.createView() }
+ }
+ .padding(.all, 16)
+ .themeColor(background: .layer2)
+ .clipShape(.rect(cornerRadius: 10))
+ }
+ }
+
+ @ViewBuilder
+ private var withdrawalSlippageInlineAlert: some View {
+ if let elevatedSlippageAmount = viewModel.elevatedSlippageAmount {
+ let withdrawalSlippageInlineAlertAttributedText = AttributedString(localizerPathKey: "APP.VAULTS.SLIPPAGE_WARNING",
+ params: ["LINK": DataLocalizer.localize(path: "APP.VAULTS.VAULT_FAQS"),
+ "AMOUNT": dydxFormatter.shared.percent(number: elevatedSlippageAmount, digits: 2) ?? "--"],
+ //TODO: Replace
+ hyperlinks: ["withdraw": "https://test.com",
+ "APP.VAULTS.VAULT_FAQS": "https://purple.com"],
+ foreground: .textPrimary)
+ InlineAlertViewModel(.init(title: nil,
+ body: withdrawalSlippageInlineAlertAttributedText,
+ level: .warning))
+ .createView()
+ }
+ }
+
+ @ViewBuilder
+ private var checkboxRow: some View {
+ if viewModel.requiresAcknowledgeHighSlippage {
+ dydxCheckboxView(isChecked: $viewModel.hasAcknowledgedHighSlippage,
+ text: DataLocalizer.localize(path: "APP.VAULTS.SLIPPAGE_ACK",
+ params: ["AMOUNT": dydxFormatter.shared.percent(number: viewModel.elevatedSlippageAmount, digits: 2) ?? "--"]))
+ }
+ }
+
+ private var submitButton: some View {
+ let content: Text
+ let state: PlatformButtonState
+ switch viewModel.submitState {
+ case .enabled:
+ state = .primary
+ content = Text(viewModel.transferType.confirmTransferText)
+ .themeColor(foreground: .textPrimary)
+ .themeFont(fontType: .base, fontSize: .large)
+ case .disabled:
+ state = .disabled
+ content = Text(DataLocalizer.localize(path: "APP.VAULTS.ACKNOWLEDGE_HIGH_SLIPPAGE"))
+ .themeColor(foreground: .textTertiary)
+ .themeFont(fontType: .base, fontSize: .large)
+ }
+ return PlatformButtonViewModel(content: content.wrappedViewModel, state: state, action: viewModel.submitAction ?? {})
+ .createView()
+ }
+}
diff --git a/dydx/dydxViews/dydxViews/_v4/Vault/DepositAndWithdrawal/dydxVaultDepositWithdrawViewModel.swift b/dydx/dydxViews/dydxViews/_v4/Vault/DepositAndWithdrawal/dydxVaultDepositWithdrawViewModel.swift
index 7df7bae60..94b8e70bc 100644
--- a/dydx/dydxViews/dydxViews/_v4/Vault/DepositAndWithdrawal/dydxVaultDepositWithdrawViewModel.swift
+++ b/dydx/dydxViews/dydxViews/_v4/Vault/DepositAndWithdrawal/dydxVaultDepositWithdrawViewModel.swift
@@ -21,7 +21,7 @@ public class dydxVaultDepositWithdrawViewModel: PlatformViewModel {
@Published public private(set) var numberFormatter = dydxNumberInputFormatter()
- @Published fileprivate var selectedTransferType: VaultTransferType
+ @Published public fileprivate(set) var selectedTransferType: VaultTransferType
@Published fileprivate var amount: Double?
fileprivate var maxAmount: Double = 0
@@ -43,39 +43,6 @@ public class dydxVaultDepositWithdrawViewModel: PlatformViewModel {
}
}
-public enum VaultTransferType: CaseIterable, RadioButtonContentDisplayable {
- case deposit
- case withdraw
-
- var displayText: String {
- switch self {
- case .deposit: return DataLocalizer.localize(path: "APP.GENERAL.DEPOSIT")
- case .withdraw: return DataLocalizer.localize(path: "APP.GENERAL.WITHDRAW")
- }
- }
-
- public var inputFieldTitle: String {
- switch self {
- case .deposit: return DataLocalizer.localize(path: "APP.VAULTS.ENTER_AMOUNT_TO_DEPOSIT")
- case .withdraw: return DataLocalizer.localize(path: "APP.VAULTS.ENTER_AMOUNT_TO_WITHDRAW")
- }
- }
-
- fileprivate var enabledText: String {
- switch self {
- case .deposit: return DataLocalizer.localize(path: "APP.VAULTS.PREVIEW_DEPOSIT")
- case .withdraw: return DataLocalizer.localize(path: "APP.VAULTS.PREVIEW_WITHDRAW")
- }
- }
-
- fileprivate var disabledText: String {
- switch self {
- case .deposit: return DataLocalizer.localize(path: "APP.VAULTS.ENTER_AMOUNT_TO_DEPOSIT")
- case .withdraw: return DataLocalizer.localize(path: "APP.VAULTS.ENTER_AMOUNT_TO_WITHDRAW")
- }
- }
-}
-
fileprivate struct VaultDepositWithdrawView: View {
@ObservedObject var viewModel: dydxVaultDepositWithdrawViewModel
@@ -142,12 +109,12 @@ fileprivate struct VaultDepositWithdrawView: View {
switch viewModel.submitState {
case .enabled:
state = .primary
- content = Text(viewModel.selectedTransferType.enabledText)
+ content = Text(viewModel.selectedTransferType.previewTransferText)
.themeColor(foreground: .textPrimary)
.themeFont(fontType: .base, fontSize: .large)
case .disabled:
state = .disabled
- content = Text(viewModel.selectedTransferType.disabledText)
+ content = Text(viewModel.selectedTransferType.needsAmountText)
.themeColor(foreground: .textTertiary)
.themeFont(fontType: .base, fontSize: .large)
}