diff --git a/PlatformRouting/PlatformRouting/_iOS/_App/MappedUIKitRouter.swift b/PlatformRouting/PlatformRouting/_iOS/_App/MappedUIKitRouter.swift index ad31939e1..e15c7576b 100644 --- a/PlatformRouting/PlatformRouting/_iOS/_App/MappedUIKitRouter.swift +++ b/PlatformRouting/PlatformRouting/_iOS/_App/MappedUIKitRouter.swift @@ -256,6 +256,8 @@ open class MappedUIKitRouter: MappedRouter { case .popup: popup(viewController, animated: animated, completion: completion) + case .presentOverFullScreen: + presentOverFullScreen(viewController, animated: animated, completion: completion) } } @@ -372,6 +374,20 @@ open class MappedUIKitRouter: MappedRouter { completion?(nil, false) } } + + private func presentOverFullScreen(_ viewController: UIViewController, animated: Bool, completion: RoutingCompletionBlock?) { + if let topmost = ViewControllerStack.shared?.topParent() { + let navigationController = UIViewController.navigation(with: viewController) + navigationController.modalPresentationStyle = .overFullScreen + //speeds up the animation by 2x + navigationController.view.layer.speed = 2 + topmost.present(navigationController, animated: animated) { + } + completion?(viewController, true) + } else { + completion?(nil, false) + } + } private func float(_ viewController: UIViewController, animated: Bool, completion: RoutingCompletionBlock?) { if let topmost = ViewControllerStack.shared?.topParent() { diff --git a/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift b/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift index 91d93b0e4..91a02aa4b 100644 --- a/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift +++ b/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift @@ -192,6 +192,10 @@ private struct StyleKeyModifier: ViewModifier { public enum MakeSheetStyle { case fullScreen, fitSize + /// note this style behaves significantly different. It takes content and rounds the corners, adds insets and safe area ignore. + /// only use on views presented with presentOverFullSreen presentation style + /// these sheets are not drag-to-dismiss + case forPresentedOverCurrentScreen } public extension View { @@ -213,7 +217,8 @@ private struct SheetViewModifier: ViewModifier { .clipShape(Capsule()) .padding(.top, topPadding) - if sheetStyle == .fullScreen { + switch sheetStyle { + case .fullScreen: return AnyView( ZStack(alignment: .top) { content @@ -223,26 +228,41 @@ private struct SheetViewModifier: ViewModifier { Spacer() } } - .environmentObject(themeSettings) + .environmentObject(themeSettings) ) - } else { + case .fitSize: return AnyView( - VStack(spacing: 0) { - Spacer() - ZStack(alignment: .top) { - content - .cornerRadius(36, corners: [.topLeft, .topRight]) - VStack { - dragIndicator + ZStack(alignment: .bottom) { + VStack(spacing: 0) { + Spacer() + ZStack(alignment: .top) { + content + .cornerRadius(36, corners: [.topLeft, .topRight]) + VStack { + dragIndicator + } } } + .environmentObject(themeSettings) } - .environmentObject(themeSettings) ) + + case .forPresentedOverCurrentScreen: + return ZStack(alignment: .bottom) { + ThemeColor.SemanticColor.layer0.color + .opacity(0.8) + content + .padding(.top, 24) + .padding(.bottom, max((content.safeAreaInsets?.bottom ?? 0), 16)) + .padding(.horizontal, 24) + .themeColor(background: .layer3) + .cornerRadius(36, corners: [.topLeft, .topRight]) + } + .ignoresSafeArea(edges: [.all]) + .wrappedInAnyView() } } } - // MARK: Make any view a button public extension View { @@ -387,7 +407,7 @@ private struct LeftAlignedModifier: ViewModifier { func body(content: Content) -> some View { HStack(spacing: 0) { content - Spacer() + Spacer(minLength: 0) } } } @@ -419,9 +439,9 @@ public extension View { private struct TopAlignedModifier: ViewModifier { func body(content: Content) -> some View { - VStack { + VStack(spacing: 0) { content - Spacer() + Spacer(minLength: 0) } } } @@ -436,8 +456,8 @@ public extension View { private struct BottomAlignedModifier: ViewModifier { func body(content: Content) -> some View { - VStack { - Spacer() + VStack(spacing: 0) { + Spacer(minLength: 0) content } } diff --git a/RoutingKit/RoutingKit/_Router/MappedRouter.swift b/RoutingKit/RoutingKit/_Router/MappedRouter.swift index 1f7a5518d..1c2189f1e 100644 --- a/RoutingKit/RoutingKit/_Router/MappedRouter.swift +++ b/RoutingKit/RoutingKit/_Router/MappedRouter.swift @@ -22,6 +22,7 @@ public enum RoutingPresentation: Int { case drawer /// center-screen popup which dims the background case popup + case presentOverFullScreen } private struct PathTuple { @@ -77,6 +78,9 @@ public class RoutingMap: NSObject, ParsingProtocol { case "popup": presentation = .popup + + case "present_over_full_screen": + presentation = .presentOverFullScreen default: presentation = nil diff --git a/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj b/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj index 18c2cdc59..f4c5b82ee 100644 --- a/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj +++ b/dydx/dydxPresenters/dydxPresenters.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ 276908FF2AAFB22F0075B2D6 /* dydxPortfolioTransfersViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276908FE2AAFB22F0075B2D6 /* dydxPortfolioTransfersViewPresenter.swift */; }; 277754352C069F8600E3E985 /* OnboardingAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277754282C069F8600E3E985 /* OnboardingAnalytics.swift */; }; 277987512BA33F15006DC5CD /* dydxSelectedMarketStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277987502BA33F15006DC5CD /* dydxSelectedMarketStore.swift */; }; + 277DB22F2C669A6800964F9B /* dydxPredictionMarketsNoticeViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277DB22E2C669A6800964F9B /* dydxPredictionMarketsNoticeViewBuilder.swift */; }; 277E8FC92B1E576B005CCBCB /* dydxProfileRewardsViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E8FC82B1E576B005CCBCB /* dydxProfileRewardsViewPresenter.swift */; }; 277E90152B1EA0E3005CCBCB /* dydxTradingRewardsViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E90142B1EA0E3005CCBCB /* dydxTradingRewardsViewPresenter.swift */; }; 277E90192B1EA3C3005CCBCB /* dydxRewardsSummaryPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E90182B1EA3C3005CCBCB /* dydxRewardsSummaryPresenter.swift */; }; @@ -533,6 +534,7 @@ 276908FE2AAFB22F0075B2D6 /* dydxPortfolioTransfersViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxPortfolioTransfersViewPresenter.swift; sourceTree = ""; }; 277754282C069F8600E3E985 /* OnboardingAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingAnalytics.swift; sourceTree = ""; }; 277987502BA33F15006DC5CD /* dydxSelectedMarketStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxSelectedMarketStore.swift; sourceTree = ""; }; + 277DB22E2C669A6800964F9B /* dydxPredictionMarketsNoticeViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxPredictionMarketsNoticeViewBuilder.swift; sourceTree = ""; }; 277E8FC82B1E576B005CCBCB /* dydxProfileRewardsViewPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = dydxProfileRewardsViewPresenter.swift; sourceTree = ""; }; 277E90142B1EA0E3005CCBCB /* dydxTradingRewardsViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxTradingRewardsViewPresenter.swift; sourceTree = ""; }; 277E90182B1EA3C3005CCBCB /* dydxRewardsSummaryPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxRewardsSummaryPresenter.swift; sourceTree = ""; }; @@ -1222,6 +1224,7 @@ 277987502BA33F15006DC5CD /* dydxSelectedMarketStore.swift */, 0274B34328F113FD005AF69E /* Components */, 02B841B128EF6C6400C4D25B /* dydxMarketInfoViewBuilder.swift */, + 277DB22E2C669A6800964F9B /* dydxPredictionMarketsNoticeViewBuilder.swift */, ); path = MarketInfo; sourceTree = ""; @@ -2066,6 +2069,7 @@ 647D0F152A9FB1C600DA7815 /* dydxFrontendAlertsProvider.swift in Sources */, 02D1345828ECF30000B46941 /* dydxMarketsSearchViewBuilder.swift in Sources */, 27044F702BBB1CDF004C750D /* dydxTakeProfitStopLossViewPresenter.swift in Sources */, + 277DB22F2C669A6800964F9B /* dydxPredictionMarketsNoticeViewBuilder.swift in Sources */, 64529A4C2AE8705E000810E6 /* dydxUpdateWorker.swift in Sources */, 278A4DA42B8FDD9D003898EB /* dydxRatingsWorker.swift in Sources */, 02DDAD55292587C600CC7531 /* QRCodeDisplayBuilder.swift in Sources */, diff --git a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json index bfe3b805e..48a4283c8 100644 --- a/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json +++ b/dydx/dydxPresenters/dydxPresenters/_Features/routing_swiftui.json @@ -293,7 +293,7 @@ "destination":"Share.storyboard", "presentation":"half" }, - "/trade/:market":{ + "/trade/:market": { "destination":"dydxPresenters.dydxMarketInfoViewBuilder", "presentation":"push" }, @@ -301,6 +301,10 @@ "destination":"dydxPresenters.dydxTradeInputViewBuilder", "presentation":"float" }, + "/trade/prediction_markets_notice": { + "destination":"dydxPresenters.dydxPredictionMarketsNoticeViewBuilder", + "presentation":"present_over_full_screen" + }, "/trade/status":{ "destination":"dydxPresenters.dydxTradeStatusViewBuilder", "presentation":"prompt" diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/KeyValueStoreProtocolStore+Ext.swift b/dydx/dydxPresenters/dydxPresenters/_v4/KeyValueStoreProtocolStore+Ext.swift index 51ace937b..e0161b66a 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/KeyValueStoreProtocolStore+Ext.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/KeyValueStoreProtocolStore+Ext.swift @@ -15,6 +15,7 @@ public enum dydxSettingsStoreKey: String, CaseIterable { case directionColorPreference = "direction_color_preference" case shouldDisplayInAppNotifications = "should_display_in_app_notifications" case gasToken = "gas_token" + case hidePredictionMarketsNoticeKey = "hide_prediction_markets_notice" public var defaultValue: Any? { switch self { @@ -23,6 +24,7 @@ public enum dydxSettingsStoreKey: String, CaseIterable { case .directionColorPreference: return "green_is_up" case .shouldDisplayInAppNotifications: return true case .gasToken: return "USDC" + case .hidePredictionMarketsNoticeKey: return false } } } diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/MarketInfo/dydxMarketInfoViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/MarketInfo/dydxMarketInfoViewBuilder.swift index 4dbaf5891..89daab598 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/MarketInfo/dydxMarketInfoViewBuilder.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/MarketInfo/dydxMarketInfoViewBuilder.swift @@ -26,23 +26,21 @@ public class dydxMarketInfoViewBuilder: NSObject, ObjectBuilderProtocol { } private class dydxMarketInfoViewController: HostingViewController { + private var hidePredictionMarketsNotice: Bool { + SettingsStore.shared?.value(forKey: dydxSettingsStoreKey.hidePredictionMarketsNoticeKey.rawValue) as? Bool ?? false + } + override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool { if request?.path == "/trade" || request?.path == "/market", let presenter = presenter as? dydxMarketInfoViewPresenter { let selectedMarketId = request?.params?["market"] as? String ?? dydxSelectedMarketsStore.shared.lastSelectedMarket dydxSelectedMarketsStore.shared.lastSelectedMarket = selectedMarketId presenter.marketId = selectedMarketId + presenter.shouldDisplayFullTradeInputOnAppear = request?.path == "/trade" if let sectionRaw = request?.params?["currentSection"] as? String { let section = PortfolioSection(rawValue: sectionRaw) ?? .positions let preselectedSection = Section.allSections.map(\.key).firstIndex(of: section) ?? 0 presenter.viewModel?.sections.onSelectionChanged?(preselectedSection) } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - if request?.path == "/trade" { - Router.shared?.navigate(to: RoutingRequest(path: "/trade/input", params: ["full": "true"]), animated: true, completion: nil) - } else { - Router.shared?.navigate(to: RoutingRequest(path: "/trade/input"), animated: true, completion: nil) - } - } return true } return false @@ -55,6 +53,7 @@ private protocol dydxMarketInfoViewPresenterProtocol: HostedViewPresenterProtoco private class dydxMarketInfoViewPresenter: HostedViewPresenter, dydxMarketInfoViewPresenterProtocol { @Published var marketId: String? + var shouldDisplayFullTradeInputOnAppear: Bool = false private let pagingPresenter = dydxMarketInfoPagingViewPresenter() private let statsPresenter = dydxMarketStatsViewPresenter() @@ -80,6 +79,10 @@ private class dydxMarketInfoViewPresenter: HostedViewPresenter() -> T? { + let presenter = dydxPredictionMarketsNoticeViewPresenter() + let view = presenter.viewModel?.createView() ?? PlatformViewModel().createView() + return dydxPredictionMarketsNoticeViewController(presenter: presenter, view: view, configuration: .default) as? T + } +} + +private class dydxPredictionMarketsNoticeViewController: HostingViewController { + override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool { + if request?.path == "/trade/prediction_markets_notice" { + return true + } + return false + } +} + +private protocol dydxPredictionMarketsNoticeViewPresenterProtocol: HostedViewPresenterProtocol { + var viewModel: dydxPredictionMarketsNoticeViewModel? { get } +} + +private class dydxPredictionMarketsNoticeViewPresenter: HostedViewPresenter, dydxPredictionMarketsNoticeViewPresenterProtocol { + + fileprivate static var hidePredictionMarketsNotice: Bool { + get { SettingsStore.shared?.value(forKey: dydxSettingsStoreKey.hidePredictionMarketsNoticeKey.rawValue) as? Bool ?? false } + set { SettingsStore.shared?.setValue(newValue, forKey: dydxSettingsStoreKey.hidePredictionMarketsNoticeKey.rawValue) } + } + + override init() { + super.init() + + viewModel = dydxPredictionMarketsNoticeViewModel() + viewModel?.continueAction = { + Router.shared?.navigate(to: RoutingRequest(path: "/action/dismiss"), animated: true, completion: nil) + } + + viewModel?.hidePredictionMarketsNotice = Self.hidePredictionMarketsNotice + viewModel?.$hidePredictionMarketsNotice + .sink { Self.hidePredictionMarketsNotice = $0 } + .store(in: &subscriptions) + } +} diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Markets/dydxMarketsViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Markets/dydxMarketsViewBuilder.swift index af6d168f8..2fde7bb07 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/Markets/dydxMarketsViewBuilder.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Markets/dydxMarketsViewBuilder.swift @@ -62,6 +62,11 @@ private class dydxMarketsViewPresenter: HostedViewPresenter, FloatingInsetProvider, FloatedDelegate, dydxTradeInputViewPresenterDelegate { override public func arrive(to request: RoutingRequest?, animated: Bool) -> Bool { - if request?.path == "/trade/input" { + if request?.path == "/trade/input", let presenter = presenter as? dydxTradeInputViewPresenter { AbacusStateManager.shared.startTrade() if request?.params?["full"] as? String == "true" { - (presenter as? dydxTradeInputViewPresenterProtocol)?.updateViewControllerPosition(position: .half) + presenter.updateViewControllerPosition(position: .half) move(to: .half) } else { - (presenter as? dydxTradeInputViewPresenterProtocol)?.updateViewControllerPosition(position: .tip) + presenter.updateViewControllerPosition(position: .tip) move(to: .tip) } - presenter?.viewModel?.editViewModel?.onScrollViewCreated = { [weak self] scrollView in + presenter.viewModel?.editViewModel?.onScrollViewCreated = { [weak self] scrollView in self?.floatTracking = scrollView } return true @@ -205,5 +205,6 @@ private class dydxTradeInputViewPresenter: HostedViewPresenter 0 ? .draft : .buySell } .store(in: &subscriptions) + } } diff --git a/dydx/dydxViews/dydxViews.xcodeproj/project.pbxproj b/dydx/dydxViews/dydxViews.xcodeproj/project.pbxproj index a60e15d32..c4522891f 100644 --- a/dydx/dydxViews/dydxViews.xcodeproj/project.pbxproj +++ b/dydx/dydxViews/dydxViews.xcodeproj/project.pbxproj @@ -181,6 +181,7 @@ 277442972AD88C4900C91357 /* Satoshi-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 277442952AD88C4900C91357 /* Satoshi-Bold.otf */; }; 277442982AD88C4900C91357 /* Satoshi-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 277442962AD88C4900C91357 /* Satoshi-Regular.otf */; }; 27759F5C2B89125F002865A9 /* dydxInlineShareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27759F5B2B89125F002865A9 /* dydxInlineShareView.swift */; }; + 277DB23E2C669AB700964F9B /* dydxPredictionMarketsNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277DB23D2C669AB700964F9B /* dydxPredictionMarketsNoticeView.swift */; }; 277E8F9F2B1A847D005CCBCB /* dydxTitledCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E8F9E2B1A847D005CCBCB /* dydxTitledCardView.swift */; }; 277E8FCB2B1E5798005CCBCB /* dydxProfileRewardsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E8FCA2B1E5798005CCBCB /* dydxProfileRewardsViewModel.swift */; }; 277E90132B1EA0D3005CCBCB /* dydxTradingRewardsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277E90122B1EA0D3005CCBCB /* dydxTradingRewardsView.swift */; }; @@ -559,6 +560,7 @@ 277442952AD88C4900C91357 /* Satoshi-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Satoshi-Bold.otf"; sourceTree = ""; }; 277442962AD88C4900C91357 /* Satoshi-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Satoshi-Regular.otf"; sourceTree = ""; }; 27759F5B2B89125F002865A9 /* dydxInlineShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxInlineShareView.swift; sourceTree = ""; }; + 277DB23D2C669AB700964F9B /* dydxPredictionMarketsNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxPredictionMarketsNoticeView.swift; sourceTree = ""; }; 277E8F9E2B1A847D005CCBCB /* dydxTitledCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxTitledCardView.swift; sourceTree = ""; }; 277E8FCA2B1E5798005CCBCB /* dydxProfileRewardsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = dydxProfileRewardsViewModel.swift; sourceTree = ""; }; 277E90122B1EA0D3005CCBCB /* dydxTradingRewardsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dydxTradingRewardsView.swift; sourceTree = ""; }; @@ -1320,6 +1322,7 @@ children = ( 02B8419E28EF68CE00C4D25B /* Components */, 02B8419C28EF68C300C4D25B /* dydxMarketInfoView.swift */, + 277DB23D2C669AB700964F9B /* dydxPredictionMarketsNoticeView.swift */, ); path = MarketInfo; sourceTree = ""; @@ -2032,6 +2035,7 @@ 02084B2D297FC2CD00CF9522 /* dydxTransferFaucetView.swift in Sources */, 022EDC8E29A048B3003D59A7 /* dydxClosePositionHeaderView.swift in Sources */, 27E0736B2C20D27F0034B963 /* dydxCancelPendingIsolatedOrdersView.swift in Sources */, + 277DB23E2C669AB700964F9B /* dydxPredictionMarketsNoticeView.swift in Sources */, 02F1D3882BEAA6CA00A9376C /* dydxTradeInputMarginView.swift in Sources */, 2700A3172C5D72BB00880AFA /* dydxVaultChartViewModel.swift in Sources */, 0256F53629AFFC9800A083C0 /* dydxOnboardConnectView.swift in Sources */, diff --git a/dydx/dydxViews/dydxViews/Media.xcassets/icon_prediction_event.imageset/Contents.json b/dydx/dydxViews/dydxViews/Media.xcassets/icon_prediction_event.imageset/Contents.json new file mode 100644 index 000000000..8b66b4baa --- /dev/null +++ b/dydx/dydxViews/dydxViews/Media.xcassets/icon_prediction_event.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icon_prediction_event.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/dydx/dydxViews/dydxViews/Media.xcassets/icon_prediction_event.imageset/icon_prediction_event.pdf b/dydx/dydxViews/dydxViews/Media.xcassets/icon_prediction_event.imageset/icon_prediction_event.pdf new file mode 100644 index 000000000..d755cd14f Binary files /dev/null and b/dydx/dydxViews/dydxViews/Media.xcassets/icon_prediction_event.imageset/icon_prediction_event.pdf differ diff --git a/dydx/dydxViews/dydxViews/Media.xcassets/icon_settlement_cash.imageset/Contents.json b/dydx/dydxViews/dydxViews/Media.xcassets/icon_settlement_cash.imageset/Contents.json new file mode 100644 index 000000000..8892b1d88 --- /dev/null +++ b/dydx/dydxViews/dydxViews/Media.xcassets/icon_settlement_cash.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icon_settlement_cash.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/dydx/dydxViews/dydxViews/Media.xcassets/icon_settlement_cash.imageset/icon_settlement_cash.pdf b/dydx/dydxViews/dydxViews/Media.xcassets/icon_settlement_cash.imageset/icon_settlement_cash.pdf new file mode 100644 index 000000000..1dafeb293 Binary files /dev/null and b/dydx/dydxViews/dydxViews/Media.xcassets/icon_settlement_cash.imageset/icon_settlement_cash.pdf differ diff --git a/dydx/dydxViews/dydxViews/_v4/MarketInfo/dydxPredictionMarketsNoticeView.swift b/dydx/dydxViews/dydxViews/_v4/MarketInfo/dydxPredictionMarketsNoticeView.swift new file mode 100644 index 000000000..3bd46e708 --- /dev/null +++ b/dydx/dydxViews/dydxViews/_v4/MarketInfo/dydxPredictionMarketsNoticeView.swift @@ -0,0 +1,121 @@ +// +// dydxPredictionMarketsNoticeView.swift +// dydxViews +// +// Created by Michael Maguire on 8/9/24. +// + +import SwiftUI +import PlatformUI +import Utilities +import Popovers + +public class dydxPredictionMarketsNoticeViewModel: PlatformViewModel { + @Published public var hidePredictionMarketsNotice = false + @Published public var continueAction: (() -> Void)? + + public init() { } + + public static var previewValue: dydxPredictionMarketsNoticeViewModel { + let vm = dydxPredictionMarketsNoticeViewModel() + 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) } + return dydxPredictionMarketsNoticeView(viewModel: self) + .wrappedInAnyView() + } + } +} + +private struct dydxPredictionMarketsNoticeView: View { + @ObservedObject var viewModel: dydxPredictionMarketsNoticeViewModel + + var title: some View { + HStack(alignment: .center, spacing: 4) { + Text(localizerPathKey: "APP.PREDICTION_MARKET.PREDICTION_MARKETS") + .themeFont(fontSize: .larger) + .themeColor(foreground: .textPrimary) + Text(localizerPathKey: "APP.GENERAL.NEW") + .themeFont(fontSize: .smaller) + .padding(.horizontal, 4) + .padding(.vertical, 2.5) + .themeColor(foreground: .colorPurple) + .themeColor(background: .colorFadedPurple) + .clipShape(.rect(cornerRadius: 4)) + } + .leftAligned() + .wrappedInAnyView() + } + + var checkboxRow: some View { + HStack(spacing: 8) { + ZStack(alignment: .center) { + ThemeColor.SemanticColor.layer0.color + PlatformIconViewModel(type: .asset(name: "icon_checked", bundle: .dydxView), + clip: .noClip, + size: .init(width: 15, height: 15), + templateColor: .textPrimary) + .createView() + .opacity(viewModel.hidePredictionMarketsNotice ? 1 : 0) + } + .frame(width: 20, height: 20) + .borderAndClip(style: .cornerRadius(6), borderColor: .borderDefault) + .onTapGesture { + viewModel.hidePredictionMarketsNotice.toggle() + } + Text(localizerPathKey: "APP.GENERAL.DONT_SHOW_AGAIN") + .themeFont(fontSize: .medium) + .themeColor(foreground: .textSecondary) + } + .leftAligned() + } + + func infoRow(imageName: String, titlePathKey: String, descriptionPathKey: String) -> some View { + HStack(alignment: .center, spacing: 8) { + PlatformIconViewModel(type: .asset(name: imageName, bundle: .dydxView), + clip: .circle(background: .layer5, spacing: 10.5, borderColor: nil), + size: CGSize(width: 48, height: 48), + templateColor: nil) + .createView() + VStack(alignment: .leading, spacing: 2) { + Text(localizerPathKey: titlePathKey) + .themeFont(fontSize: .large) + .themeColor(foreground: .textSecondary) + Text(localizerPathKey: descriptionPathKey) + .themeFont(fontSize: .medium) + .themeColor(foreground: .textTertiary) + } + } + .leftAligned() + } + + var continueButton: some View { + let buttonContent = + Text(DataLocalizer.localize(path: "APP.COMPLIANCE_MODAL.CONTINUE")) + .wrappedViewModel + return PlatformButtonViewModel(content: buttonContent) { + viewModel.continueAction?() + } + .createView() + } + + var body: some View { + VStack(spacing: 16) { + title + VStack(spacing: 24) { + infoRow(imageName: "icon_settlement_cash", + titlePathKey: "APP.PREDICTION_MARKET.LEVERAGE_TRADE_EVENT_OUTCOMES_TITLE", + descriptionPathKey: "APP.PREDICTION_MARKET.LEVERAGE_TRADE_EVENT_OUTCOMES_DESCRIPTION") + infoRow(imageName: "icon_prediction_event", + titlePathKey: "APP.PREDICTION_MARKET.SETTLEMENT_OUTCOMES_TITLE", + descriptionPathKey: "APP.PREDICTION_MARKET.SETTLEMENT_OUTCOMES_DESCRIPTION") + checkboxRow + continueButton + } + } + .makeSheet(sheetStyle: .forPresentedOverCurrentScreen) + } +} diff --git a/dydx/dydxViews/dydxViews/_v4/Markets/dydxMarketsView.swift b/dydx/dydxViews/dydxViews/_v4/Markets/dydxMarketsView.swift index d8671e107..1c4ab6fa4 100644 --- a/dydx/dydxViews/dydxViews/_v4/Markets/dydxMarketsView.swift +++ b/dydx/dydxViews/dydxViews/_v4/Markets/dydxMarketsView.swift @@ -22,6 +22,7 @@ public class dydxMarketsViewModel: PlatformViewModel { @Published public var banner: dydxMarketsBannerViewModel? @Published public var summary = dydxMarketSummaryViewModel() @Published public var filter = dydxMarketAssetFilterViewModel() + @Published public var filterFooterText: String? @Published public var sort = dydxMarketAssetSortViewModel() @Published public var assetList: dydxMarketAssetListViewModel? = dydxMarketAssetListViewModel() @Published public var scrollAction: ScrollAction = .none @@ -64,9 +65,16 @@ public class dydxMarketsViewModel: PlatformViewModel { let header = VStack(spacing: 0) { - self.filter.createView(parentStyle: style) - .padding(.horizontal, 16) - Spacer() + VStack(spacing: 8) { + self.filter.createView(parentStyle: style) + .padding(.horizontal, 16) + if let filterFooterText = self.filterFooterText { + Text(filterFooterText) + .themeFont(fontType: .base, fontSize: .small) + .themeColor(foreground: .textTertiary) + } + } + Spacer(minLength: 16) self.sort.createView(parentStyle: style) .padding(.leading, 16) Spacer(minLength: 12)