From 0526bd6517349cc34fd48241dfe8eb9dd6018d0a Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Wed, 4 Dec 2024 10:18:22 +0100 Subject: [PATCH] Add foundation for new connection view --- ios/MullvadVPN.xcodeproj/project.pbxproj | 36 +++++++- .../Extensions/UIColor+Helpers.swift | 6 +- ios/MullvadVPN/UI appearance/UIMetrics.swift | 4 + .../FeatureIndicators/ConnectionView.swift | 87 +++++++++++++++++++ ios/MullvadVPN/Views/MainButton.swift | 34 ++++++++ ios/MullvadVPN/Views/MainButtonStyle.swift | 49 +++++++++++ ios/MullvadVPN/Views/SplitMainButton.swift | 58 +++++++++++++ ios/MullvadVPN/Views/VisualEffectView.swift | 21 +++++ 8 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift create mode 100644 ios/MullvadVPN/Views/MainButton.swift create mode 100644 ios/MullvadVPN/Views/MainButtonStyle.swift create mode 100644 ios/MullvadVPN/Views/SplitMainButton.swift create mode 100644 ios/MullvadVPN/Views/VisualEffectView.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 05a800581dcf..36ca68285350 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -462,6 +462,8 @@ 7A09C98129D99215000C2CAC /* String+FuzzyMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A09C98029D99215000C2CAC /* String+FuzzyMatch.swift */; }; 7A0B311E2B303A0D004B12E0 /* AccessbilityIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0B311D2B303A0D004B12E0 /* AccessbilityIdentifier.swift */; }; 7A0C0F632A979C4A0058EFCE /* Coordinator+Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0C0F622A979C4A0058EFCE /* Coordinator+Router.swift */; }; + 7A0EAE9A2D01B41500D3EB8B /* MainButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0EAE992D01B41500D3EB8B /* MainButtonStyle.swift */; }; + 7A0EAE9E2D01BCBF00D3EB8B /* View+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0EAE9D2D01BCBF00D3EB8B /* View+Size.swift */; }; 7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5802EBC42A8E44AC00E5CE4C /* AppRoutes.swift */; }; 7A12D0762B062D5C00E9602D /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */; }; 7A12D0772B062D6500E9602D /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */; }; @@ -563,7 +565,6 @@ 7A8A19052CE4E9A9000BCB5B /* SwitchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19042CE4E9A5000BCB5B /* SwitchRowView.swift */; }; 7A8A19072CE4E9D3000BCB5B /* SettingsInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19062CE4E9CC000BCB5B /* SettingsInfoView.swift */; }; 7A8A190A2CE5FFE9000BCB5B /* SettingsDAITAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19092CE5FFDF000BCB5B /* SettingsDAITAView.swift */; }; - 7A8A190C2CE618D3000BCB5B /* View+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A190B2CE618CE000BCB5B /* View+Size.swift */; }; 7A8A190E2CEB77C1000BCB5B /* SettingsRowViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A190D2CEB77B7000BCB5B /* SettingsRowViewFooter.swift */; }; 7A8A19102CEE391B000BCB5B /* RowSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A190F2CEE3918000BCB5B /* RowSeparator.swift */; }; 7A8A19122CEF1E68000BCB5B /* SettingsInfoContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A19112CEF1E58000BCB5B /* SettingsInfoContainerView.swift */; }; @@ -605,6 +606,10 @@ 7A9F293D2CAD2FD5005F2089 /* InfoModalConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F293C2CAD2FCF005F2089 /* InfoModalConfig.swift */; }; 7A9FA1422A2E3306000B728D /* CheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1412A2E3306000B728D /* CheckboxView.swift */; }; 7A9FA1442A2E3FE5000B728D /* CheckableSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */; }; + 7AA130992CFF365D00640DF9 /* ConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA130982CFF365A00640DF9 /* ConnectionView.swift */; }; + 7AA1309B2D0048D800640DF9 /* MainButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA1309A2D0048D800640DF9 /* MainButton.swift */; }; + 7AA1309F2D007B2500640DF9 /* VisualEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA1309E2D007B2500640DF9 /* VisualEffectView.swift */; }; + 7AA130A12D01B1E200640DF9 /* SplitMainButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA130A02D01B1E200640DF9 /* SplitMainButton.swift */; }; 7AA513862BC91C6B00D081A4 /* LogRotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */; }; 7AA7046A2C8EFE2B0045699D /* StoredRelays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA704682C8EFE050045699D /* StoredRelays.swift */; }; 7AB2B6702BA1EB8C00B03E3B /* ListCustomListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB2B66E2BA1EB8C00B03E3B /* ListCustomListViewController.swift */; }; @@ -1834,6 +1839,8 @@ 7A09C98029D99215000C2CAC /* String+FuzzyMatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+FuzzyMatch.swift"; sourceTree = ""; }; 7A0B311D2B303A0D004B12E0 /* AccessbilityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessbilityIdentifier.swift; sourceTree = ""; }; 7A0C0F622A979C4A0058EFCE /* Coordinator+Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Coordinator+Router.swift"; sourceTree = ""; }; + 7A0EAE992D01B41500D3EB8B /* MainButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainButtonStyle.swift; sourceTree = ""; }; + 7A0EAE9D2D01BCBF00D3EB8B /* View+Size.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Size.swift"; sourceTree = ""; }; 7A12D0752B062D5C00E9602D /* URLSessionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionProtocol.swift; sourceTree = ""; }; 7A1A26422A2612AE00B978AA /* PaymentAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentAlertPresenter.swift; sourceTree = ""; }; 7A1A26442A29CEF700B978AA /* RelayFilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterViewController.swift; sourceTree = ""; }; @@ -1923,7 +1930,6 @@ 7A8A19042CE4E9A5000BCB5B /* SwitchRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchRowView.swift; sourceTree = ""; }; 7A8A19062CE4E9CC000BCB5B /* SettingsInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInfoView.swift; sourceTree = ""; }; 7A8A19092CE5FFDF000BCB5B /* SettingsDAITAView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDAITAView.swift; sourceTree = ""; }; - 7A8A190B2CE618CE000BCB5B /* View+Size.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Size.swift"; sourceTree = ""; }; 7A8A190D2CEB77B7000BCB5B /* SettingsRowViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowViewFooter.swift; sourceTree = ""; }; 7A8A190F2CEE3918000BCB5B /* RowSeparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowSeparator.swift; sourceTree = ""; }; 7A8A19112CEF1E58000BCB5B /* SettingsInfoContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInfoContainerView.swift; sourceTree = ""; }; @@ -1962,6 +1968,10 @@ 7A9F293C2CAD2FCF005F2089 /* InfoModalConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoModalConfig.swift; sourceTree = ""; }; 7A9FA1412A2E3306000B728D /* CheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxView.swift; sourceTree = ""; }; 7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckableSettingsCell.swift; sourceTree = ""; }; + 7AA130982CFF365A00640DF9 /* ConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionView.swift; sourceTree = ""; }; + 7AA1309A2D0048D800640DF9 /* MainButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainButton.swift; sourceTree = ""; }; + 7AA1309E2D007B2500640DF9 /* VisualEffectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectView.swift; sourceTree = ""; }; + 7AA130A02D01B1E200640DF9 /* SplitMainButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitMainButton.swift; sourceTree = ""; }; 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogRotationTests.swift; sourceTree = ""; }; 7AA704682C8EFE050045699D /* StoredRelays.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredRelays.swift; sourceTree = ""; }; 7AB2B66E2BA1EB8C00B03E3B /* ListCustomListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCustomListViewController.swift; sourceTree = ""; }; @@ -2982,6 +2992,7 @@ 583FE01E29C197D5006E85F9 /* Tunnel */ = { isa = PBXGroup; children = ( + 7AA130972CFF364F00640DF9 /* FeatureIndicators */, 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */, 5878A27A2909649A0096FC88 /* CustomOverlayRenderer.swift */, 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */, @@ -3012,10 +3023,14 @@ F03580242A13842C00E5DAFD /* IncreasedHitButton.swift */, 7A9F29382CABFAEC005F2089 /* InfoHeaderView.swift */, 7A5869942B32E9C700640D27 /* LinkButton.swift */, + 7AA1309A2D0048D800640DF9 /* MainButton.swift */, + 7A0EAE992D01B41500D3EB8B /* MainButtonStyle.swift */, 7A8A190F2CEE3918000BCB5B /* RowSeparator.swift */, 58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */, + 7AA130A02D01B1E200640DF9 /* SplitMainButton.swift */, E1FD0DF428AA7CE400299DB4 /* StatusActivityView.swift */, 58EF581025D69DB400AEBA94 /* StatusImageView.swift */, + 7AA1309E2D007B2500640DF9 /* VisualEffectView.swift */, ); path = Views; sourceTree = ""; @@ -3091,7 +3106,7 @@ 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */, 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */, 7A516C2D2B6D357500BBD33D /* URL+Scoping.swift */, - 7A8A190B2CE618CE000BCB5B /* View+Size.swift */, + 7A0EAE9D2D01BCBF00D3EB8B /* View+Size.swift */, 7A8A18FA2CE4B66C000BCB5B /* View+TapAreaSize.swift */, ); path = Extensions; @@ -4024,6 +4039,14 @@ path = SelectLocation; sourceTree = ""; }; + 7AA130972CFF364F00640DF9 /* FeatureIndicators */ = { + isa = PBXGroup; + children = ( + 7AA130982CFF365A00640DF9 /* ConnectionView.swift */, + ); + path = FeatureIndicators; + sourceTree = ""; + }; 7AD63A422CDA661B00445268 /* Extensions */ = { isa = PBXGroup; children = ( @@ -5830,6 +5853,7 @@ 7A8A19282CF603EB000BCB5B /* SettingsViewControllerFactory.swift in Sources */, 58B26E2A2943545A00D5980C /* NotificationManagerDelegate.swift in Sources */, 7A8A19072CE4E9D3000BCB5B /* SettingsInfoView.swift in Sources */, + 7AA1309B2D0048D800640DF9 /* MainButton.swift in Sources */, 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */, 5878A27B2909649A0096FC88 /* CustomOverlayRenderer.swift in Sources */, 7A8A19052CE4E9A9000BCB5B /* SwitchRowView.swift in Sources */, @@ -5900,6 +5924,7 @@ 586C0D872B03D39600E7CDD7 /* AccessMethodCellReuseIdentifier.swift in Sources */, 7A9CCCBD2A96302800DD6A34 /* LoginCoordinator.swift in Sources */, 7A7B3AB62C6DE4DA00D4BCCE /* RestorePurchasesView.swift in Sources */, + 7A0EAE9E2D01BCBF00D3EB8B /* View+Size.swift in Sources */, 58293FB125124117005D0BB5 /* CustomTextField.swift in Sources */, F09A29822A9F8AD200EA3B6F /* RedeemVoucherInteractor.swift in Sources */, 58138E61294871C600684F0C /* DeviceDataThrottling.swift in Sources */, @@ -5924,7 +5949,10 @@ 586A950E290125F3007BAF2B /* ProductsRequestOperation.swift in Sources */, 7AF9BE902A39F26000DBFEDB /* Collection+Sorting.swift in Sources */, 58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */, + 7A0EAE9A2D01B41500D3EB8B /* MainButtonStyle.swift in Sources */, 58CEB3022AFD365600E6E088 /* SwitchCellContentConfiguration.swift in Sources */, + 7AA130A12D01B1E200640DF9 /* SplitMainButton.swift in Sources */, + 7AA1309F2D007B2500640DF9 /* VisualEffectView.swift in Sources */, 7A9CCCB52A96302800DD6A34 /* AddCreditSucceededCoordinator.swift in Sources */, 7A0C0F632A979C4A0058EFCE /* Coordinator+Router.swift in Sources */, 7A6F2FAB2AFD3097006D0856 /* CustomDNSCellFactory.swift in Sources */, @@ -6038,7 +6066,6 @@ 58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */, 586C0D832B03D2FF00E7CDD7 /* ShadowsocksSectionHandler.swift in Sources */, 58B26E262943522400D5980C /* NotificationProvider.swift in Sources */, - 7A8A190C2CE618D3000BCB5B /* View+Size.swift in Sources */, 58CE5E64224146200008646E /* AppDelegate.swift in Sources */, F0DA87492A9CBA9F006044F1 /* AccountDeviceRow.swift in Sources */, 58FF9FE42B075BDD00E4C97D /* EditAccessMethodItemIdentifier.swift in Sources */, @@ -6068,6 +6095,7 @@ 58EFC7712AFB45E500E9F4CB /* SettingsChildCoordinator.swift in Sources */, 7A8A19102CEE391B000BCB5B /* RowSeparator.swift in Sources */, 58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */, + 7AA130992CFF365D00640DF9 /* ConnectionView.swift in Sources */, F0E8CC0A2A4EE127007ED3B4 /* SetupAccountCompletedContentView.swift in Sources */, 581DA2752A1E283E0046ED47 /* WgKeyRotation.swift in Sources */, 5827B0BB2B14A28300CCBBA1 /* MethodTestingStatusCellContentView.swift in Sources */, diff --git a/ios/MullvadVPN/Extensions/UIColor+Helpers.swift b/ios/MullvadVPN/Extensions/UIColor+Helpers.swift index f3c89ba88974..0157058b89f2 100644 --- a/ios/MullvadVPN/Extensions/UIColor+Helpers.swift +++ b/ios/MullvadVPN/Extensions/UIColor+Helpers.swift @@ -6,9 +6,13 @@ // Copyright © 2019 Mullvad VPN AB. All rights reserved. // -import UIKit +import SwiftUI extension UIColor { + var color: Color { + Color(self) + } + /// Returns the color lighter by the given percent (in range from 0..1) func lightened(by percent: CGFloat) -> UIColor? { darkened(by: -percent) diff --git a/ios/MullvadVPN/UI appearance/UIMetrics.swift b/ios/MullvadVPN/UI appearance/UIMetrics.swift index e68d8b0c3207..1d0f98e8f06c 100644 --- a/ios/MullvadVPN/UI appearance/UIMetrics.swift +++ b/ios/MullvadVPN/UI appearance/UIMetrics.swift @@ -134,6 +134,10 @@ enum UIMetrics { static let inRowHeight: CGFloat = 22 static let outRowHeight: CGFloat = 44 } + + enum MainButton { + static let cornerRadius: CGFloat = 4 + } } extension UIMetrics { diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift new file mode 100644 index 000000000000..3f3d4e473bde --- /dev/null +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView.swift @@ -0,0 +1,87 @@ +// +// ConnectionView.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-12-03. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +// TODO: Replace all hardcoded values with real values dependent on tunnel state. To be addressed in upcoming PR. + +struct ConnectionView: View { + var body: some View { + ZStack { + BlurView() + + VStack(alignment: .leading, spacing: 16) { + ConnectionPanel() + ButtonPanel() + } + .padding(16) + } + .cornerRadius(12) + .padding(16) + // Importing UIView in SwitftUI (see BlurView) has sizing limitations, so we need to help the view + // understand its width constraints. + .frame(maxWidth: UIScreen.main.bounds.width) + } +} + +#Preview { + ConnectionView() + .background(UIColor.secondaryColor.color) +} + +private struct BlurView: View { + var body: some View { + Spacer() + .overlay { + VisualEffectView(effect: UIBlurEffect(style: .dark)) + .opacity(0.8) + } + } +} + +private struct ConnectionPanel: View { + var body: some View { + VStack(alignment: .leading) { + Text("Connected") + .textCase(.uppercase) + .font(.title3.weight(.semibold)) + .foregroundStyle(UIColor.successColor.color) + .padding(.bottom, 4) + Text("Country, City") + .font(.title3.weight(.semibold)) + .foregroundStyle(UIColor.primaryTextColor.color) + Text("Server") + .font(.body) + .foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6)) + } + } +} + +private struct ButtonPanel: View { + var body: some View { + VStack(spacing: 16) { + SplitMainButton( + text: "Switch location", + image: .iconReload, + style: .default, + primaryAction: { + print("Switch location tapped") + }, secondaryAction: { + print("Reload tapped") + } + ) + + MainButton( + text: "Cancel", + style: .danger + ) { + print("Cancel tapped") + } + } + } +} diff --git a/ios/MullvadVPN/Views/MainButton.swift b/ios/MullvadVPN/Views/MainButton.swift new file mode 100644 index 000000000000..a5d6f0f7186b --- /dev/null +++ b/ios/MullvadVPN/Views/MainButton.swift @@ -0,0 +1,34 @@ +// +// MainButton.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-12-04. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct MainButton: View { + var text: String + var style: MainButtonStyle.Style + + var action: () -> Void + + var body: some View { + Button(action: action, label: { + HStack { + Spacer() + Text(text) + Spacer() + } + }) + .buttonStyle(MainButtonStyle(style)) + .cornerRadius(UIMetrics.MainButton.cornerRadius) + } +} + +#Preview { + MainButton(text: "Connect", style: .default) { + print("Tapped") + } +} diff --git a/ios/MullvadVPN/Views/MainButtonStyle.swift b/ios/MullvadVPN/Views/MainButtonStyle.swift new file mode 100644 index 000000000000..06a32b560689 --- /dev/null +++ b/ios/MullvadVPN/Views/MainButtonStyle.swift @@ -0,0 +1,49 @@ +// +// MainButtonStyle.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct MainButtonStyle: ButtonStyle { + @State var style: Style + + init(_ style: Style) { + self.style = style + } + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(.horizontal, 8) + .frame(height: 44) + .foregroundColor( + configuration.isPressed + ? UIColor.secondaryTextColor.color + : UIColor.primaryTextColor.color + ) + .background(style.color) + .font(.body.weight(.semibold)) + } +} + +extension MainButtonStyle { + enum Style { + case `default` + case danger + case success + + var color: Color { + switch self { + case .default: + Color(UIColor.primaryColor) + case .danger: + Color(UIColor.dangerColor) + case .success: + Color(UIColor.successColor) + } + } + } +} diff --git a/ios/MullvadVPN/Views/SplitMainButton.swift b/ios/MullvadVPN/Views/SplitMainButton.swift new file mode 100644 index 000000000000..59a5d1b2eafc --- /dev/null +++ b/ios/MullvadVPN/Views/SplitMainButton.swift @@ -0,0 +1,58 @@ +// +// SplitMainButton.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-12-05. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct SplitMainButton: View { + var text: String + var image: ImageResource + var style: MainButtonStyle.Style + + var primaryAction: () -> Void + var secondaryAction: () -> Void + + @State private var width: CGFloat = 0 + + var body: some View { + HStack(spacing: 1) { + Button(action: primaryAction, label: { + HStack { + Spacer() + Text(text) + Spacer() + } + .padding(.trailing, -width) + }) + Button(action: secondaryAction, label: { + Image(image) + .resizable() + .scaledToFit() + .padding(4) + }) + .aspectRatio(1, contentMode: .fit) + .sizeOfView { width = $0.width } + } + .buttonStyle(MainButtonStyle(style)) + .cornerRadius(UIMetrics.MainButton.cornerRadius) + } +} + +#Preview { + SplitMainButton( + text: "Connect", + image: .iconReload, + style: .default, + primaryAction: { + print("Tapped primary") + }, + secondaryAction: { + print("Tapped secondary") + } + ) + .frame(maxWidth: .infinity) +} diff --git a/ios/MullvadVPN/Views/VisualEffectView.swift b/ios/MullvadVPN/Views/VisualEffectView.swift new file mode 100644 index 000000000000..0cad1b06d710 --- /dev/null +++ b/ios/MullvadVPN/Views/VisualEffectView.swift @@ -0,0 +1,21 @@ +// +// VisualEffectView.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-12-04. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +struct VisualEffectView: UIViewRepresentable { + var effect: UIVisualEffect? + + func makeUIView(context: UIViewRepresentableContext) -> UIVisualEffectView { + UIVisualEffectView() + } + + func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext) { + uiView.effect = effect + } +}