From b66db4c031e679e7e5ebb5c42e72d5819b27abfc Mon Sep 17 00:00:00 2001 From: mike-dydx Date: Mon, 27 Nov 2023 16:32:59 -0500 Subject: [PATCH] move export secret and etherscan lookup to wallet connection screen --- .../Theme/ThemeViewModifiers.swift | 54 ++++++++++- .../dydxAddressDetailsViewBuilder.swift | 15 --- .../dydxProfileHeaderViewPresenter.swift | 2 +- .../_v4/Wallet/Wallets2ViewBuilder.swift | 12 +++ .../dydxStateManager/AbacusStateManager.swift | 5 +- .../Components/dydxProfileHeaderView.swift | 10 +- .../_v4/Wallet/WalletConnectionView.swift | 93 +++++++++++++------ .../dydxViews/_v4/Wallet/Wallets2View.swift | 2 +- 8 files changed, 136 insertions(+), 57 deletions(-) diff --git a/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift b/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift index cc87aa241..eef42dce6 100644 --- a/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift +++ b/PlatformUI/PlatformUI/DesignSystem/Theme/ThemeViewModifiers.swift @@ -17,7 +17,7 @@ public extension View { } func themeColor(background: ThemeColor.SemanticColor) -> some View { - modifier(SemanticColorModifier(layerColor: background)) + modifier(BackgroundColorModifier(layerColor: background)) } func themeGradient(background: ThemeColor.SemanticColor, gradientColor: Color) -> some View { @@ -31,7 +31,7 @@ public extension Text { } } -struct TextColorModifier: ViewModifier { +private struct TextColorModifier: ViewModifier { @EnvironmentObject var themeSettings: ThemeSettings let textColor: ThemeColor.SemanticColor @@ -42,7 +42,7 @@ struct TextColorModifier: ViewModifier { } } -struct SemanticColorModifier: ViewModifier { +private struct BackgroundColorModifier: ViewModifier { @EnvironmentObject var themeSettings: ThemeSettings let layerColor: ThemeColor.SemanticColor @@ -53,7 +53,18 @@ struct SemanticColorModifier: ViewModifier { } } -struct GradientColorModifier: ViewModifier { +private struct BorderColorModifier: ViewModifier { + @EnvironmentObject var themeSettings: ThemeSettings + + let layerColor: ThemeColor.SemanticColor + + func body(content: Content) -> some View { + content + .background(themeSettings.themeConfig.themeColor.color(of: layerColor)) + } +} + +private struct GradientColorModifier: ViewModifier { @EnvironmentObject var themeSettings: ThemeSettings let layerColor: ThemeColor.SemanticColor @@ -268,8 +279,43 @@ public extension View { func border(borderWidth: CGFloat = 1, cornerRadius: CGFloat = 0, borderColor: Color? = ThemeColor.SemanticColor.layer5.color) -> some View { modifier(BorderModifier(cornerRadius: cornerRadius, borderWidth: borderWidth, borderColor: borderColor)) } + + func borderAndClip(style: BorderAndClipStyle, borderColor: ThemeColor.SemanticColor, lineWidth: CGFloat) -> some View { + modifier(BorderAndClipModifier(style: style, borderColor: borderColor, lineWidth: lineWidth)) + } } +/// The clip shape/style +public enum BorderAndClipStyle { + /// A rectangular shape with rounded corners with specified corner radius, aligned inside the frame of the view containing it. + case cornerRadius(CGFloat) + /// A capsule shape is equivalent to a rounded rectangle where the corner radius is chosen as half the length of the rectangle’s smallest edge. + case capsule +} + +private struct BorderAndClipModifier: ViewModifier { + let style: BorderAndClipStyle + let borderColor: ThemeColor.SemanticColor + let lineWidth: CGFloat + + func body(content: Content) -> some View { + switch style { + case .cornerRadius(let cornerRadius): + content + .clipShape(RoundedRectangle(cornerSize: .init(width: cornerRadius, height: cornerRadius))) + .overlay(RoundedRectangle(cornerRadius: cornerRadius) + .strokeBorder(borderColor.color, lineWidth: lineWidth)) + + case .capsule: + content + .clipShape(Capsule()) + .overlay(Capsule() + .strokeBorder(borderColor.color, lineWidth: lineWidth)) + } + } +} + + private struct BorderModifier: ViewModifier { var cornerRadius: CGFloat = .infinity var borderWidth: CGFloat = 1 diff --git a/dydx/dydxPresenters/dydxPresenters/_v4/Profile/AddressDetails/dydxAddressDetailsViewBuilder.swift b/dydx/dydxPresenters/dydxPresenters/_v4/Profile/AddressDetails/dydxAddressDetailsViewBuilder.swift index e7c19527d..e1a588ffe 100644 --- a/dydx/dydxPresenters/dydxPresenters/_v4/Profile/AddressDetails/dydxAddressDetailsViewBuilder.swift +++ b/dydx/dydxPresenters/dydxPresenters/_v4/Profile/AddressDetails/dydxAddressDetailsViewBuilder.swift @@ -68,21 +68,6 @@ private class dydxAddressDetailsViewPresenter: HostedViewPresenter { } } + viewModel.openInEtherscanTapped = { + let ethereumAddress = wallet.ethereumAddress + let urlString = "https://etherscan.io/address/\(ethereumAddress)" + if let url = URL(string: urlString), URLHandler.shared?.canOpenURL(url) ?? false { + URLHandler.shared?.open(url, completionHandler: nil) + } + } + + viewModel.exportSecretPhraseTapped = { + Router.shared?.navigate(to: RoutingRequest(url: "/my-profile/keyexport"), animated: true, completion: nil) + } + viewModel.walletImageUrl = wallet.imageUrl // TODO: diff --git a/dydx/dydxStateManager/dydxStateManager/AbacusStateManager.swift b/dydx/dydxStateManager/dydxStateManager/AbacusStateManager.swift index ad781b759..a7f8580c7 100644 --- a/dydx/dydxStateManager/dydxStateManager/AbacusStateManager.swift +++ b/dydx/dydxStateManager/dydxStateManager/AbacusStateManager.swift @@ -14,7 +14,10 @@ import dydxFormatter public final class AbacusStateManager: NSObject { public static let shared = AbacusStateManager() - public let deploymentUri = dydxStringFeatureFlag.deployment_url.string ?? (CredientialConfig.shared.key(for: "webAppUrl"))! + public let deploymentUri = { + let url = dydxStringFeatureFlag.deployment_url.string ?? (CredientialConfig.shared.key(for: "webAppUrl"))! + return url.last == "/" ? url : url + "/" + }() public var isMainNet: Bool { asyncStateManager.environment?.isMainNet ?? false diff --git a/dydx/dydxViews/dydxViews/_v4/Profile/Components/dydxProfileHeaderView.swift b/dydx/dydxViews/dydxViews/_v4/Profile/Components/dydxProfileHeaderView.swift index d0538a2d6..7098bc837 100644 --- a/dydx/dydxViews/dydxViews/_v4/Profile/Components/dydxProfileHeaderView.swift +++ b/dydx/dydxViews/dydxViews/_v4/Profile/Components/dydxProfileHeaderView.swift @@ -14,7 +14,7 @@ public class dydxProfileHeaderViewModel: PlatformViewModel { @Published public var dydxChainLogoUrl: URL? @Published public var dydxAddress: String? @Published public var seeMoreInfoAction: (() -> Void)? - @Published public var switchWalletAction: (() -> Void)? + @Published public var manageWalletAction: (() -> Void)? public init() { } @@ -34,14 +34,14 @@ public class dydxProfileHeaderViewModel: PlatformViewModel { clip: .noClip, size: .init(width: 14, height: 8), templateColor: .textTertiary) - let switchWalletButton = HStack(spacing: 9) { - Text(DataLocalizer.localize(path: "APP.GENERAL.SWITCH_WALLET")) + let manageWalletButton = HStack(spacing: 9) { + Text(DataLocalizer.localize(path: "APP.GENERAL.MANAGE_WALLET")) .themeFont(fontSize: .small) .themeColor(foreground: .textTertiary) dropDownIcon.createView(parentStyle: parentStyle) } .onTapGesture { [weak self] in - self?.switchWalletAction?() + self?.manageWalletAction?() } let addressInfoView = VStack(alignment: .leading, spacing: 4) { @@ -63,7 +63,7 @@ public class dydxProfileHeaderViewModel: PlatformViewModel { .createView(parentStyle: parentStyle) Spacer() if self.dydxAddress?.isEmpty == false { - switchWalletButton + manageWalletButton } } HStack { diff --git a/dydx/dydxViews/dydxViews/_v4/Wallet/WalletConnectionView.swift b/dydx/dydxViews/dydxViews/_v4/Wallet/WalletConnectionView.swift index 000d4ea84..48ce66863 100644 --- a/dydx/dydxViews/dydxViews/_v4/Wallet/WalletConnectionView.swift +++ b/dydx/dydxViews/dydxViews/_v4/Wallet/WalletConnectionView.swift @@ -18,6 +18,8 @@ public class WalletConnectionViewModel: PlatformViewModel { @Published public var walletImageUrl: URL? @Published public var pnl24hPercent: SignedAmountViewModel? @Published public var onTap: (() -> Void)? + @Published public var openInEtherscanTapped: (() -> Void)? + @Published public var exportSecretPhraseTapped: (() -> Void)? public init() {} @@ -35,45 +37,76 @@ public class WalletConnectionViewModel: PlatformViewModel { PlatformView(viewModel: self, parentStyle: parentStyle, styleKey: styleKey) { [weak self] style in AnyView( Group { - let icon = PlatformIconViewModel(type: .url(url: self?.walletImageUrl), clip: .defaultCircle) + let buttonBorderWidth = 1.5 + let buttonContentVerticalPadding: CGFloat = 8 + let buttonContentHorizontalPadding: CGFloat = 12 + + let icon = self?.walletImageUrl == nil ? nil : PlatformIconViewModel(type: .url(url: self?.walletImageUrl), clip: .defaultCircle) .createView(parentStyle: style) let status = PlatformIconViewModel(type: .asset(name: "status_filled", bundle: Bundle.dydxView), - size: CGSize(width: 8, height: 8), - templateColor: self?.templateColor ?? .textTertiary) + size: CGSize(width: 8, height: 8), + templateColor: self?.templateColor ?? .textTertiary) .createView(parentStyle: style) - let main = - Text(self?.walletAddress ?? "") + let addressText = Text(self?.walletAddress ?? "") + .lineLimit(1) + .truncationMode(.middle) + .themeFont(fontSize: .medium) + .themeColor(foreground: .textPrimary) + + let blockExplorerButton = Button(action: { + self?.openInEtherscanTapped?() + }, label: { + HStack(spacing: 4) { + Text(DataLocalizer.shared?.localize(path: "APP.GENERAL.BLOCK_EXPLORER", params: nil) ?? "") + .lineLimit(1) + .themeFont(fontSize: .medium) + .themeColor(foreground: .textTertiary) + PlatformIconViewModel(type: .asset(name: "icon_external_link", + bundle: .dydxView), + clip: .noClip, + size: .init(width: 20, height: 20), + templateColor: .textTertiary) + .createView(parentStyle: style) + } + .fixedSize() + .padding(.horizontal, buttonContentHorizontalPadding) + .padding(.vertical, buttonContentVerticalPadding) + .themeColor(background: .layer3) + }) + .borderAndClip(style: .capsule, borderColor: .layer6, lineWidth: buttonBorderWidth) + + let exportPhraseButton = Button(action: { + self?.exportSecretPhraseTapped?() + }, label: { + Text(DataLocalizer.shared?.localize(path: "APP.MNEMONIC_EXPORT.EXPORT_PHRASE", params: nil) ?? "") .lineLimit(1) - .truncationMode(.middle) .themeFont(fontSize: .medium) - .themeColor(foreground: .textPrimary) - - let trailing = - VStack(alignment: .trailing, spacing: 2) { - Text(self?.equity ?? "") - .themeFont(fontType: .number, fontSize: .small) - if let pnl24hPercent = self?.pnl24hPercent { - HStack { - Text(DataLocalizer.localize(path: "APP.GENERAL.TIME_STRINGS.24H", params: nil)) - .themeFont(fontSize: .smaller) - pnl24hPercent.createView(parentStyle: style - .themeColor(foreground: .textTertiary) - .themeFont(fontType: .number, fontSize: .smaller)) - } + .themeColor(foreground: .colorRed) + .padding(.horizontal, buttonContentHorizontalPadding) + .padding(.vertical, buttonContentVerticalPadding) + }) + .themeColor(background: .colorFadedRed) + .borderAndClip(style: .capsule, borderColor: .borderDestructive, lineWidth: buttonBorderWidth) + + let main = VStack(alignment: .leading) { + addressText + if self?.selected == true { + HStack(spacing: 10) { + blockExplorerButton + exportPhraseButton } } - - Group { - PlatformTableViewCellViewModel(leading: status.wrappedViewModel, - logo: icon.wrappedViewModel, - main: main.wrappedViewModel, - trailing: trailing.wrappedViewModel) - .createView(parentStyle: style) } - .frame(width: UIScreen.main.bounds.width - 32, height: 64) - .themeColor(background: .layer5) - .cornerRadius(16) + .padding(.vertical, 16) + + PlatformTableViewCellViewModel(leading: status.wrappedViewModel, + logo: icon?.wrappedViewModel, + main: main.wrappedViewModel) + .createView(parentStyle: style) + .frame(width: UIScreen.main.bounds.width - 32) + .themeColor(background: self?.selected == true ? .layer1 : .layer3) + .borderAndClip(style: .cornerRadius(10), borderColor: .borderDefault, lineWidth: 1) .onTapGesture { self?.onTap?() } diff --git a/dydx/dydxViews/dydxViews/_v4/Wallet/Wallets2View.swift b/dydx/dydxViews/dydxViews/_v4/Wallet/Wallets2View.swift index 0fc5d155d..6a6353d72 100644 --- a/dydx/dydxViews/dydxViews/_v4/Wallet/Wallets2View.swift +++ b/dydx/dydxViews/dydxViews/_v4/Wallet/Wallets2View.swift @@ -29,7 +29,7 @@ public class Wallets2ViewModel: PlatformViewModel { guard let self = self else { return AnyView(PlatformView.nilView) } let view = VStack(alignment: .leading, spacing: 16) { - Text(DataLocalizer.localize(path: "APP.GENERAL.SWITCH_WALLET", params: nil)) + Text(DataLocalizer.localize(path: "APP.GENERAL.MANAGE_WALLET", params: nil)) .themeFont(fontType: .bold, fontSize: .largest) .padding(.top, 40)