diff --git a/nym-vpn/ios/Home/Sources/Home/HomeView.swift b/nym-vpn/ios/Home/Sources/Home/HomeView.swift index 2595ea630a..6d32fe2842 100644 --- a/nym-vpn/ios/Home/Sources/Home/HomeView.swift +++ b/nym-vpn/ios/Home/Sources/Home/HomeView.swift @@ -1,10 +1,15 @@ import SwiftUI -import UIComponents +import AppSettings +import Modifiers import Theme +import UIComponents public struct HomeView: View { - @ObservedObject private var viewModel = HomeViewModel(selectedNetwork: .mixnet) - public init() {} + @ObservedObject private var viewModel: HomeViewModel + + public init(viewModel: HomeViewModel) { + self.viewModel = viewModel + } public var body: some View { HomeFlowCoordinator(state: viewModel, content: content) @@ -23,6 +28,7 @@ private extension HomeView { connectionSection() connectButton() } + .appearanceUpdate() .frame(maxWidth: .infinity, maxHeight: .infinity) .background { NymColor.background @@ -34,8 +40,7 @@ private extension HomeView { func navbar() -> some View { CustomNavBar( title: "NymVPN".localizedString, - rightButton: CustomNavBarButton(type: .settings, action: { print("settings") - viewModel.navigateToSettings() }) + rightButton: CustomNavBarButton(type: .settings, action: { viewModel.navigateToSettings() }) ) Spacer() .frame(height: 50) diff --git a/nym-vpn/ios/NymVPN.xcodeproj/project.pbxproj b/nym-vpn/ios/NymVPN.xcodeproj/project.pbxproj index 637f99bb83..ad093d5c16 100644 --- a/nym-vpn/ios/NymVPN.xcodeproj/project.pbxproj +++ b/nym-vpn/ios/NymVPN.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ D99A147A2B357E9900F2728B /* NymVPNApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D99A14792B357E9900F2728B /* NymVPNApp.swift */; }; D99A148A2B35A73F00F2728B /* Home in Frameworks */ = {isa = PBXBuildFile; productRef = D99A14892B35A73F00F2728B /* Home */; }; D99A148C2B35A74200F2728B /* Theme in Frameworks */ = {isa = PBXBuildFile; productRef = D99A148B2B35A74200F2728B /* Theme */; }; + D9CD835D2B6C32D4008DA864 /* Modifiers in Frameworks */ = {isa = PBXBuildFile; productRef = D9CD835C2B6C32D4008DA864 /* Modifiers */; }; D9F699E02B4C1862005576A9 /* Settings in Frameworks */ = {isa = PBXBuildFile; productRef = D9F699DF2B4C1862005576A9 /* Settings */; }; /* End PBXBuildFile section */ @@ -76,6 +77,7 @@ D968BDC92B6AEA9D003F42E5 /* AppSettings in Frameworks */, D99A148C2B35A74200F2728B /* Theme in Frameworks */, D99A148A2B35A73F00F2728B /* Home in Frameworks */, + D9CD835D2B6C32D4008DA864 /* Modifiers in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -181,6 +183,7 @@ D96740222B56A67A001F6891 /* Tunnels */, D968BDC82B6AEA9D003F42E5 /* AppSettings */, D968BDCA2B6BC98E003F42E5 /* AppVersionProvider */, + D9CD835C2B6C32D4008DA864 /* Modifiers */, ); productName = NymVPN; productReference = D99A14762B357E9900F2728B /* NymVPN.app */; @@ -605,6 +608,10 @@ isa = XCSwiftPackageProductDependency; productName = Theme; }; + D9CD835C2B6C32D4008DA864 /* Modifiers */ = { + isa = XCSwiftPackageProductDependency; + productName = Modifiers; + }; D9F699DF2B4C1862005576A9 /* Settings */ = { isa = XCSwiftPackageProductDependency; productName = Settings; diff --git a/nym-vpn/ios/NymVPN/NymVPNApp.swift b/nym-vpn/ios/NymVPN/NymVPNApp.swift index 6155f480ec..b7c16eb783 100644 --- a/nym-vpn/ios/NymVPN/NymVPNApp.swift +++ b/nym-vpn/ios/NymVPN/NymVPNApp.swift @@ -6,6 +6,7 @@ import Tunnels @main struct NymVPNApp: App { + init() { setup() } @@ -13,9 +14,8 @@ struct NymVPNApp: App { var body: some Scene { WindowGroup { NavigationStack { - HomeView() + HomeView(viewModel: HomeViewModel(selectedNetwork: .mixnet)) } - .preferredColorScheme(AppSettings.shared.currentTheme.colorScheme) .environmentObject(AppSettings.shared) .environmentObject(TunnelsManager.shared) } diff --git a/nym-vpn/ios/Services/Package.swift b/nym-vpn/ios/Services/Package.swift index 069b0cf84a..def4919afc 100644 --- a/nym-vpn/ios/Services/Package.swift +++ b/nym-vpn/ios/Services/Package.swift @@ -18,6 +18,10 @@ let package = Package( name: "AppVersionProvider", targets: ["AppVersionProvider"] ), + .library( + name: "Modifiers", + targets: ["Modifiers"] + ), .library( name: "Tunnels", targets: ["Tunnels"] @@ -34,6 +38,13 @@ let package = Package( dependencies: [], path: "Sources/Services/AppVersionProvider" ), + .target( + name: "Modifiers", + dependencies: [ + "AppSettings" + ], + path: "Sources/Services/Modifiers" + ), .target( name: "Tunnels", dependencies: [], diff --git a/nym-vpn/ios/Services/Sources/Services/AppSettings/AppSettings.swift b/nym-vpn/ios/Services/Sources/Services/AppSettings/AppSettings.swift index 2b8d9ff05f..0a94ad63ac 100644 --- a/nym-vpn/ios/Services/Sources/Services/AppSettings/AppSettings.swift +++ b/nym-vpn/ios/Services/Sources/Services/AppSettings/AppSettings.swift @@ -3,5 +3,5 @@ import SwiftUI public final class AppSettings: ObservableObject { public static let shared = AppSettings() - @AppStorage("currentTheme") public var currentTheme: Theme = .automatic + @AppStorage("currentAppearance") public var currentTheme: AppSetting.Appearance = .automatic } diff --git a/nym-vpn/ios/Services/Sources/Services/AppSettings/Settings/AppSetting.swift b/nym-vpn/ios/Services/Sources/Services/AppSettings/Settings/AppSetting.swift new file mode 100644 index 0000000000..cd1fe55799 --- /dev/null +++ b/nym-vpn/ios/Services/Sources/Services/AppSettings/Settings/AppSetting.swift @@ -0,0 +1,20 @@ +import SwiftUI + +public struct AppSetting { + public enum Appearance: Int, CaseIterable { + case automatic + case light + case dark + + public var colorScheme: ColorScheme? { + switch self { + case .light: + return .light + case .dark: + return .dark + case .automatic: + return ColorScheme(.unspecified) + } + } + } +} diff --git a/nym-vpn/ios/Services/Sources/Services/AppSettings/Settings/Theme.swift b/nym-vpn/ios/Services/Sources/Services/AppSettings/Settings/Theme.swift deleted file mode 100644 index 58fe022c8f..0000000000 --- a/nym-vpn/ios/Services/Sources/Services/AppSettings/Settings/Theme.swift +++ /dev/null @@ -1,18 +0,0 @@ -import SwiftUI - -public enum Theme: Int { - case light - case dark - case automatic - - public var colorScheme: ColorScheme? { - switch self { - case .light: - return .light - case .dark: - return .dark - case .automatic: - return ColorScheme(.unspecified) - } - } -} diff --git a/nym-vpn/ios/Services/Sources/Services/Modifiers/AppearanceUpdate.swift b/nym-vpn/ios/Services/Sources/Services/Modifiers/AppearanceUpdate.swift new file mode 100644 index 0000000000..5f355ee71d --- /dev/null +++ b/nym-vpn/ios/Services/Sources/Services/Modifiers/AppearanceUpdate.swift @@ -0,0 +1,17 @@ +import SwiftUI +import AppSettings + +public struct AppearanceUpdate: ViewModifier { + @EnvironmentObject private var appSettings: AppSettings + + public func body(content: Content) -> some View { + content + .preferredColorScheme(appSettings.currentTheme.colorScheme) + } +} + +public extension View { + func appearanceUpdate() -> some View { + modifier(AppearanceUpdate()) + } +} diff --git a/nym-vpn/ios/Settings/Package.swift b/nym-vpn/ios/Settings/Package.swift index 40c7a21f5e..77683573d1 100644 --- a/nym-vpn/ios/Settings/Package.swift +++ b/nym-vpn/ios/Settings/Package.swift @@ -16,16 +16,12 @@ let package = Package( ) ], dependencies: [ -// .package(name: "AppVersionProvider", path: "../Services"), -// .package(name: "AppSettings", path: "../Services"), .package(path: "../UIComponents") ], targets: [ .target( name: "Settings", dependencies: [ -// "AppVersionProvider", -// "AppSettings", "UIComponents" ] ), diff --git a/nym-vpn/ios/Settings/Sources/Settings/SettingsConfig.swift b/nym-vpn/ios/Settings/Sources/Settings/SettingsConfig.swift deleted file mode 100644 index fa73e6d77a..0000000000 --- a/nym-vpn/ios/Settings/Sources/Settings/SettingsConfig.swift +++ /dev/null @@ -1,98 +0,0 @@ -import Theme -import UIComponents - -public struct SettingsConfig { - public init() {} - - var sections: [SettingsSection] { - [ - connectionSection(), - themeSection(), - logsSection(), - feedbackSection(), - legalSection() - ] - } -} - -private extension SettingsConfig { - func connectionSection() -> SettingsSection { - .connection( - viewModels: [ - SettingsListItemViewModel( - accessory: .arrow, - title: "autoConnectTitle".localizedString, - subtitle: "autoConnectSubtitle".localizedString, - imageName: "autoConnect", - action: {} - ), - SettingsListItemViewModel( - accessory: .toggle, - title: "entryLocationTitle".localizedString, - subtitle: "entryLocationSubtitle".localizedString, - imageName: "entryHop", - action: {} - ) - ] - ) - } - - func themeSection() -> SettingsSection { - .theme( - viewModels: [ - SettingsListItemViewModel( - accessory: .arrow, - title: "displayTheme".localizedString, - imageName: "displayTheme", - action: { - - } - ) - ] - ) - } - - func logsSection() -> SettingsSection { - .logs( - viewModels: [ - SettingsListItemViewModel( - accessory: .arrow, - title: "logs".localizedString, - imageName: "logs", - action: {} - ) - ] - ) - } - - func feedbackSection() -> SettingsSection { - .feedback( - viewModels: [ - SettingsListItemViewModel( - accessory: .arrow, - title: "feedback".localizedString, - imageName: "feedback", - action: {} - ), - SettingsListItemViewModel( - accessory: .arrow, - title: "support".localizedString, - imageName: "support", - action: {} - ) - ] - ) - } - - func legalSection() -> SettingsSection { - .legal( - viewModels: [ - SettingsListItemViewModel( - accessory: .arrow, - title: "legal".localizedString, - action: {} - ) - ] - ) - } -} diff --git a/nym-vpn/ios/Settings/Sources/Settings/SettingsFlowCoordinator.swift b/nym-vpn/ios/Settings/Sources/Settings/SettingsFlowCoordinator.swift index 90c849e8b0..f46b1e3b7b 100644 --- a/nym-vpn/ios/Settings/Sources/Settings/SettingsFlowCoordinator.swift +++ b/nym-vpn/ios/Settings/Sources/Settings/SettingsFlowCoordinator.swift @@ -1,4 +1,5 @@ import SwiftUI +import AppSettings struct SettingsFlowCoordinator: View { @ObservedObject var state: SettingsFlowState @@ -12,7 +13,7 @@ struct SettingsFlowCoordinator: View { @ViewBuilder private func linkDestination(link: SettingsLink) -> some View { switch link { case .theme: - SettingsThemeView() + AppearanceView(viewModel: AppearanceViewModel(path: $state.path, appSettings: AppSettings.shared)) } } } diff --git a/nym-vpn/ios/Settings/Sources/Settings/SettingsView.swift b/nym-vpn/ios/Settings/Sources/Settings/SettingsView.swift index af42f407c7..1b57d511e2 100644 --- a/nym-vpn/ios/Settings/Sources/Settings/SettingsView.swift +++ b/nym-vpn/ios/Settings/Sources/Settings/SettingsView.swift @@ -1,9 +1,11 @@ import SwiftUI -import Theme +import AppSettings +import Modifiers import UIComponents +import Theme public struct SettingsView: View { - @StateObject var viewModel: SettingsViewModel + @StateObject private var viewModel: SettingsViewModel public init(viewModel: SettingsViewModel) { _viewModel = StateObject(wrappedValue: viewModel) @@ -22,6 +24,7 @@ private extension SettingsView { settingsList() Spacer() } + .appearanceUpdate() .navigationBarBackButtonHidden(true) .frame(maxWidth: .infinity, maxHeight: .infinity) .ignoresSafeArea(edges: [.bottom]) @@ -44,7 +47,7 @@ private extension SettingsView { SettingsList( viewModel: SettingsListViewModel( - sections: viewModel.settingsConfig.sections, + sections: viewModel.sections, appVersion: viewModel.appVersion() ) ) diff --git a/nym-vpn/ios/Settings/Sources/Settings/SettingsViewModel.swift b/nym-vpn/ios/Settings/Sources/Settings/SettingsViewModel.swift index 14f843ce62..23f1d08a5a 100644 --- a/nym-vpn/ios/Settings/Sources/Settings/SettingsViewModel.swift +++ b/nym-vpn/ios/Settings/Sources/Settings/SettingsViewModel.swift @@ -1,9 +1,19 @@ import Foundation import AppVersionProvider +import UIComponents public class SettingsViewModel: SettingsFlowState { let settingsTitle = "settings".localizedString - let settingsConfig = SettingsConfig() + + var sections: [SettingsSection] { + [ + connectionSection(), + themeSection(), + logsSection(), + feedbackSection(), + legalSection() + ] + } func navigateHome() { path = .init() @@ -19,3 +29,85 @@ private extension SettingsViewModel { path.append(SettingsLink.theme) } } + +private extension SettingsViewModel { + func connectionSection() -> SettingsSection { + .connection( + viewModels: [ + SettingsListItemViewModel( + accessory: .arrow, + title: "autoConnectTitle".localizedString, + subtitle: "autoConnectSubtitle".localizedString, + imageName: "autoConnect", + action: {} + ), + SettingsListItemViewModel( + accessory: .toggle, + title: "entryLocationTitle".localizedString, + subtitle: "entryLocationSubtitle".localizedString, + imageName: "entryHop", + action: {} + ) + ] + ) + } + + func themeSection() -> SettingsSection { + .theme( + viewModels: [ + SettingsListItemViewModel( + accessory: .arrow, + title: "displayTheme".localizedString, + imageName: "displayTheme", + action: { [weak self] in + self?.navigateToTheme() + } + ) + ] + ) + } + + func logsSection() -> SettingsSection { + .logs( + viewModels: [ + SettingsListItemViewModel( + accessory: .arrow, + title: "logs".localizedString, + imageName: "logs", + action: {} + ) + ] + ) + } + + func feedbackSection() -> SettingsSection { + .feedback( + viewModels: [ + SettingsListItemViewModel( + accessory: .arrow, + title: "feedback".localizedString, + imageName: "feedback", + action: {} + ), + SettingsListItemViewModel( + accessory: .arrow, + title: "support".localizedString, + imageName: "support", + action: {} + ) + ] + ) + } + + func legalSection() -> SettingsSection { + .legal( + viewModels: [ + SettingsListItemViewModel( + accessory: .arrow, + title: "legal".localizedString, + action: {} + ) + ] + ) + } +} diff --git a/nym-vpn/ios/Settings/Sources/Settings/Theme/AppearanceView.swift b/nym-vpn/ios/Settings/Sources/Settings/Theme/AppearanceView.swift new file mode 100644 index 0000000000..4bd4dc1744 --- /dev/null +++ b/nym-vpn/ios/Settings/Sources/Settings/Theme/AppearanceView.swift @@ -0,0 +1,57 @@ +import SwiftUI +import AppSettings +import Modifiers +import Theme +import UIComponents + +public struct AppearanceView: View { + private let viewModel: AppearanceViewModel + + public init(viewModel: AppearanceViewModel) { + self.viewModel = viewModel + } + + public var body: some View { + VStack { + navbar() + themeOptions() + Spacer() + } + .appearanceUpdate() + .navigationBarBackButtonHidden(true) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .ignoresSafeArea(edges: [.bottom]) + .background { + NymColor.background + .ignoresSafeArea() + } + } +} + +private extension AppearanceView { + @ViewBuilder + func navbar() -> some View { + CustomNavBar( + title: viewModel.title, + leftButton: CustomNavBarButton(type: .back, action: { viewModel.navigateBack() }) + ) + } + + @ViewBuilder + func themeOptions() -> some View { + ForEach(viewModel.themes, id: \.self) { theme in + SettingButton( + viewModel: + SettingButtonViewModel( + title: viewModel.themeTitle(for: theme), + subtitle: viewModel.themeSubtitle(for: theme), + isSelected: viewModel.isSelected(for: theme) + ) + ) + .onTapGesture { + viewModel.setCurrentTheme(with: theme) + } + .padding(EdgeInsets(top: 24, leading: 16, bottom: 0, trailing: 16)) + } + } +} diff --git a/nym-vpn/ios/Settings/Sources/Settings/Theme/AppearanceViewModel.swift b/nym-vpn/ios/Settings/Sources/Settings/Theme/AppearanceViewModel.swift new file mode 100644 index 0000000000..d24eea2c89 --- /dev/null +++ b/nym-vpn/ios/Settings/Sources/Settings/Theme/AppearanceViewModel.swift @@ -0,0 +1,60 @@ +import SwiftUI +import AppSettings +import Theme + +public struct AppearanceViewModel { + private let appSettings: AppSettings + + let title = "displayTheme".localizedString + + @Binding var path: NavigationPath + + var themes: [AppSetting.Appearance] { + AppSetting.Appearance.allCases + } + + var currentTheme: AppSetting.Appearance { + appSettings.currentTheme + } + + public init(path: Binding, appSettings: AppSettings) { + _path = path + self.appSettings = appSettings + } + + func setCurrentTheme(with theme: AppSetting.Appearance) { + appSettings.currentTheme = theme + } +} + +extension AppearanceViewModel { + func themeTitle(for theme: AppSetting.Appearance) -> String { + switch theme { + case .light: + return "lightThemeTitle".localizedString + case .dark: + return "darkThemeTitle".localizedString + case .automatic: + return "automaticThemeTitle".localizedString + } + } + + func themeSubtitle(for theme: AppSetting.Appearance) -> String? { + switch theme { + case .light, .dark: + return nil + case .automatic: + return "automaticThemeSubtitle".localizedString + } + } + + func isSelected(for theme: AppSetting.Appearance) -> Bool { + appSettings.currentTheme == theme + } +} + +extension AppearanceViewModel { + func navigateBack() { + path.removeLast() + } +} diff --git a/nym-vpn/ios/Settings/Sources/Settings/Theme/SettingsThemeView.swift b/nym-vpn/ios/Settings/Sources/Settings/Theme/SettingsThemeView.swift deleted file mode 100644 index a1b20dad7b..0000000000 --- a/nym-vpn/ios/Settings/Sources/Settings/Theme/SettingsThemeView.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -public struct SettingsThemeView: View { -// private let viewModel: SettingsThemeViewModel -// -// public init(viewModel: SettingsThemeViewModel) { -// self.viewModel = viewModel -// } - - public var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} diff --git a/nym-vpn/ios/Settings/Sources/Settings/Theme/SettingsThemeViewModel.swift b/nym-vpn/ios/Settings/Sources/Settings/Theme/SettingsThemeViewModel.swift deleted file mode 100644 index 60a678f778..0000000000 --- a/nym-vpn/ios/Settings/Sources/Settings/Theme/SettingsThemeViewModel.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -public struct SettingsThemeViewModel { -// private let appSettings: AppSettings -// -// public init(appSettings: AppSettings) { -// self.appSettings = appSettings -// } -} diff --git a/nym-vpn/ios/Theme/Sources/Theme/Resources/Localizable.xcstrings b/nym-vpn/ios/Theme/Sources/Theme/Resources/Localizable.xcstrings index bbec98f3f8..58058370da 100644 --- a/nym-vpn/ios/Theme/Sources/Theme/Resources/Localizable.xcstrings +++ b/nym-vpn/ios/Theme/Sources/Theme/Resources/Localizable.xcstrings @@ -67,6 +67,28 @@ } } }, + "automaticThemeSubtitle" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use device theme" + } + } + } + }, + "automaticThemeTitle" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatic" + } + } + } + }, "connect" : { "extractionState" : "manual", "localizations" : { @@ -111,6 +133,17 @@ } } }, + "darkThemeTitle" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dark theme" + } + } + } + }, "disconnected" : { "extractionState" : "manual", "localizations" : { @@ -243,6 +276,17 @@ } } }, + "lightThemeTitle" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Light theme" + } + } + } + }, "logs" : { "extractionState" : "manual", "localizations" : { diff --git a/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButton.swift b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButton.swift index 0c9fe4a933..1932420455 100644 --- a/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButton.swift +++ b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButton.swift @@ -41,62 +41,3 @@ public struct NetworkButton: View { ) } } - -public struct NetworkButtonViewModel { - public enum ButtonType { - case mixnet - case wireguard - - var imageName: String { - switch self { - case .mixnet: - return "mixnetIcon" - case .wireguard: - return "wireguardIcon" - } - } - - var title: String { - switch self { - case .mixnet: - "5hopMixnetTitle".localizedString - case .wireguard: - "2hopWireGuardTitle".localizedString - } - } - - var subtitle: String { - switch self { - case .mixnet: - "5hopMixnetSubtitle".localizedString - case .wireguard: - "2hopWireGuardSubtitle".localizedString - } - } - } - - let type: ButtonType - - @Binding var selectedNetwork: ButtonType - - public init(type: ButtonType, selectedNetwork: Binding) { - self.type = type - self._selectedNetwork = selectedNetwork - } - - private var isSelected: Bool { - type == selectedNetwork - } - - var selectionImageName: String { - isSelected ? "networkSelectedCircle" : "networkCircle" - } - - var selectionImageColor: Color { - isSelected ? NymColor.primaryOrange : NymColor.networkButtonCircle - } - - var selectionStrokeColor: Color { - isSelected ? NymColor.primaryOrange : .clear - } -} diff --git a/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButtonViewModel.swift b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButtonViewModel.swift new file mode 100644 index 0000000000..07051895a3 --- /dev/null +++ b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/NetworkButton/NetworkButtonViewModel.swift @@ -0,0 +1,61 @@ +import SwiftUI +import Theme + +public struct NetworkButtonViewModel { + public enum ButtonType { + case mixnet + case wireguard + + var imageName: String { + switch self { + case .mixnet: + return "mixnetIcon" + case .wireguard: + return "wireguardIcon" + } + } + + var title: String { + switch self { + case .mixnet: + "5hopMixnetTitle".localizedString + case .wireguard: + "2hopWireGuardTitle".localizedString + } + } + + var subtitle: String { + switch self { + case .mixnet: + "5hopMixnetSubtitle".localizedString + case .wireguard: + "2hopWireGuardSubtitle".localizedString + } + } + } + + let type: ButtonType + + @Binding var selectedNetwork: ButtonType + + public init(type: ButtonType, selectedNetwork: Binding) { + self.type = type + self._selectedNetwork = selectedNetwork + } + + private var isSelected: Bool { + type == selectedNetwork + } + + var selectionImageName: String { + isSelected ? "networkSelectedCircle" : "networkCircle" + } + + var selectionImageColor: Color { + isSelected ? NymColor.primaryOrange : NymColor.networkButtonCircle + } + + var selectionStrokeColor: Color { + isSelected ? NymColor.primaryOrange : .clear + } +} diff --git a/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/SettingButton/SettingButton.swift b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/SettingButton/SettingButton.swift new file mode 100644 index 0000000000..0233b6339a --- /dev/null +++ b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/SettingButton/SettingButton.swift @@ -0,0 +1,41 @@ +import SwiftUI +import Theme + +public struct SettingButton: View { + private let viewModel: SettingButtonViewModel + + public init(viewModel: SettingButtonViewModel) { + self.viewModel = viewModel + } + + public var body: some View { + VStack { + HStack { + Image(viewModel.selectionImageName, bundle: .module) + .foregroundStyle(viewModel.selectionImageColor) + .padding(.leading, 16) + + VStack(alignment: .leading) { + Text(viewModel.title) + .foregroundStyle(NymColor.sysOnSurface) + .textStyle(.Body.Large.primary) + if let subtitle = viewModel.subtitle { + Text(subtitle) + .foregroundStyle(NymColor.sysOutline) + .textStyle(.Body.Medium.primary) + } + } + .padding(.leading, 8) + Spacer() + } + } + .frame(maxWidth: .infinity, minHeight: 64, maxHeight: 64) + .background(NymColor.navigationBarBackground) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .inset(by: 0.5) + .stroke(viewModel.selectionStrokeColor) + ) + } +} diff --git a/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/SettingButton/SettingButtonViewModel.swift b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/SettingButton/SettingButtonViewModel.swift new file mode 100644 index 0000000000..d63489e539 --- /dev/null +++ b/nym-vpn/ios/UIComponents/Sources/UIComponents/Buttons/SettingButton/SettingButtonViewModel.swift @@ -0,0 +1,26 @@ +import SwiftUI +import Theme + +public struct SettingButtonViewModel { + let title: String + let subtitle: String? + let isSelected: Bool + + public init(title: String, subtitle: String?, isSelected: Bool) { + self.title = title + self.subtitle = subtitle + self.isSelected = isSelected + } + + var selectionStrokeColor: Color { + isSelected ? NymColor.primaryOrange : .clear + } + + var selectionImageName: String { + isSelected ? "networkSelectedCircle" : "networkCircle" + } + + var selectionImageColor: Color { + isSelected ? NymColor.primaryOrange : NymColor.networkButtonCircle + } +} diff --git a/nym-vpn/ios/UIComponents/Sources/UIComponents/SettingsList/SettingsListItem.swift b/nym-vpn/ios/UIComponents/Sources/UIComponents/SettingsList/SettingsListItem.swift index 30b6b78841..61dd76d02c 100644 --- a/nym-vpn/ios/UIComponents/Sources/UIComponents/SettingsList/SettingsListItem.swift +++ b/nym-vpn/ios/UIComponents/Sources/UIComponents/SettingsList/SettingsListItem.swift @@ -34,6 +34,9 @@ public struct SettingsListItem: View { ) ) .padding(.horizontal, 16) + .onTapGesture { + viewModel.action() + } } }