diff --git a/PlatformUI/PlatformUI.xcodeproj/project.pbxproj b/PlatformUI/PlatformUI.xcodeproj/project.pbxproj index 1278b7a32..ab5b72ed3 100644 --- a/PlatformUI/PlatformUI.xcodeproj/project.pbxproj +++ b/PlatformUI/PlatformUI.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 02F38BF22A9AAE3700969E06 /* CircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F38BF12A9AAE3700969E06 /* CircularProgressView.swift */; }; 1C811E336064517E256D1290 /* Pods_iOS_PlatformUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6793D8074DF2E7FE4592138 /* Pods_iOS_PlatformUITests.framework */; }; 27044F882BBB2ADF004C750D /* Text+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27044F872BBB2ADF004C750D /* Text+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 */; }; 64A4DC5F29677BCB008D8E20 /* PlatformOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A4DC5E29677BCB008D8E20 /* PlatformOutput.swift */; }; @@ -134,6 +135,7 @@ 02F16FE128B53A200085DC58 /* SampleStyleLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleStyleLabel.swift; sourceTree = ""; }; 02F38BF12A9AAE3700969E06 /* CircularProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressView.swift; sourceTree = ""; }; 27044F872BBB2ADF004C750D /* Text+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Text+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 = ""; }; 418D5C02425B3C680BF32DA4 /* Pods_iOS_PlatformUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOS_PlatformUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -205,6 +207,7 @@ children = ( 024B794F28B6D95D00F7C386 /* SignedAmount.swift */, 027A7B44291E090F00DF402D /* ColoredText.swift */, + 277E7AC52BBF3BE8009F95DE /* InlineAlert.swift */, ); path = Labels; sourceTree = ""; @@ -654,6 +657,7 @@ 023788F528B9924D00F212E1 /* PlatformButton.swift in Sources */, 02E2C9C128A2C22B00F7C3BE /* SampleThemeLabel.swift in Sources */, 0243A73729BB8DBB00A083FE /* PlatformListViewModel.swift in Sources */, + 277E7AC62BBF3BE8009F95DE /* InlineAlert.swift in Sources */, 02F16FDB28B491EE0085DC58 /* PlatformUI.swift in Sources */, 64A4DC5F29677BCB008D8E20 /* PlatformOutput.swift in Sources */, 0243A73E29BE2D7C00A083FE /* Divider.swift in Sources */, diff --git a/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift b/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift new file mode 100644 index 000000000..fa9aeec2e --- /dev/null +++ b/PlatformUI/PlatformUI/Components/Labels/InlineAlert.swift @@ -0,0 +1,112 @@ +// +// InlineAlert.swift +// dydxUI +// +// Created by Michael Maguire on 4/4/24. +// Copyright © 2024 dYdX Trading Inc. All rights reserved. +// + +import SwiftUI + +public class InlineAlertViewModel: PlatformViewModel { + + @Published public var config: Config + + public init(_ config: Config) { + self.config = config + } + + public static var previewValue: InlineAlertViewModel = { + let vm = InlineAlertViewModel(Config(title: "Title", body: "Body", level: .error)) + return vm + }() + + 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) } + let config = self.config + + return HStack(spacing: 0) { + config.level.tabColor.color + .frame(width: 6) + HStack(spacing: 0) { + VStack(alignment: .leading) { + Text(config.title) + .themeColor(foreground: .textPrimary) + .themeFont(fontType: .plus, fontSize: .medium) + Text(config.body) + .themeColor(foreground: .textPrimary) + .themeFont(fontType: .base, fontSize: .small) + } + Spacer() + } + .padding(.all, 10) + .themeColor(background: config.level.backgroundColor) + } + + .fixedSize(horizontal: false, vertical: true) + .clipShape(.rect(cornerRadius: 6)) + .wrappedInAnyView() + } + } +} + +public extension InlineAlertViewModel { + struct Config { + public var title: String + public var body: String + public var level: Level + + public init(title: String, body: String, level: Level) { + self.title = title + self.body = body + self.level = level + } + } +} + +public extension InlineAlertViewModel { + enum Level { + case error + case warning + case success + + fileprivate var tabColor: ThemeColor.SemanticColor { + switch self { + case .error: + return .colorRed + case .warning: + return .colorYellow + case .success: + return .colorGreen + } + } + + fileprivate var backgroundColor: ThemeColor.SemanticColor { + switch self { + case .error: + return .colorFadedRed + case .warning: + return .colorFadedYellow + case .success: + return .colorFadedGreen + } + } + } +} + +#if DEBUG +struct InlineAlert_Previews: PreviewProvider { + @StateObject static var themeSettings = ThemeSettings.shared + + static var previews: some View { + Group { + InlineAlertViewModel.previewValue + .createView() + .environmentObject(themeSettings) + .previewLayout(.sizeThatFits) + } + } +} +#endif + diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift b/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift index c1615c49d..f1a4ffedd 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/TakeProfitStopLoss/dydxTakeProfitStopLossViewPresenter.swift @@ -48,8 +48,8 @@ private class dydxTakeProfitStopLossViewPresenter: HostedViewPresenter { statePublisher .map(\.?.input?.triggerOrders) - .throttle(for: .milliseconds(10), scheduler: DispatchQueue.main, latest: true) .removeDuplicates() + .throttle(for: .milliseconds(10), scheduler: DispatchQueue.main, latest: true) .share() .eraseToAnyPublisher() } diff --git a/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift b/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift index 9d25cc31f..a10c781f6 100644 --- a/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift +++ b/dydx/dydxViews/dydxViews/_v4/TakeProfitStopLoss/Components/dydxTakeProfitStopLossInputAreaViewModel.swift @@ -15,10 +15,12 @@ public class dydxTakeProfitStopLossInputAreaModel: PlatformViewModel { @Published public var numOpenTakeProfitOrders: Int? @Published public var takeProfitPriceInputViewModel: dydxTriggerPriceInputViewModel? @Published public var gainInputViewModel: dydxGainLossInputViewModel? + @Published public var takeProfitAlert: InlineAlertViewModel? @Published public var numOpenStopLossOrders: Int? @Published public var stopLossPriceInputViewModel: dydxTriggerPriceInputViewModel? @Published public var lossInputViewModel: dydxGainLossInputViewModel? + @Published public var stopLossAlert: InlineAlertViewModel? @Published public var multipleOrdersExistViewModel: dydxMultipleOrdersExistViewModel? private var hasMultipleTakeProfitOrders: Bool { (numOpenTakeProfitOrders ?? 0) > 1 } @@ -141,6 +143,7 @@ public class dydxTakeProfitStopLossInputAreaModel: PlatformViewModel { self.gainInputViewModel?.createView(parentStyle: parentStyle, styleKey: styleKey) } } + self.takeProfitAlert?.createView(parentStyle: parentStyle, styleKey: styleKey) } VStack(alignment: .leading, spacing: 16) { self.createSectionHeader(triggerType: .stopLoss) @@ -152,6 +155,7 @@ public class dydxTakeProfitStopLossInputAreaModel: PlatformViewModel { self.lossInputViewModel?.createView(parentStyle: parentStyle, styleKey: styleKey) } } + self.stopLossAlert?.createView(parentStyle: parentStyle, styleKey: styleKey) } } .wrappedInAnyView()