diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index db751975..b98f5f51 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 0EC066D12C7DC47600D88A94 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EC066D02C7DC47600D88A94 /* LaunchScreen.storyboard */; platformFilter = ios; }; 0EC332CA2B8A1808000B9C2F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */; }; 0EC332D22B8A1808000B9C2F /* PassepartoutTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 0EC4A7132CD597EF00B7CAAD /* AppUIPlatform in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC4A7122CD597EF00B7CAAD /* AppUIPlatform */; }; 0EC797422B9378E000C093B7 /* Shared+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797402B9378E000C093B7 /* Shared+App.swift */; }; 0EC797432B9378E000C093B7 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797412B9378E000C093B7 /* Shared.swift */; }; 0EC797442B93790600C093B7 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797412B9378E000C093B7 /* Shared.swift */; }; @@ -27,8 +28,6 @@ 0EDE56EA2CABE40D0082D21C /* Intents.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0EDE56E62CABE40D0082D21C /* Intents.plist */; }; 0EDE56FA2CABE42E0082D21C /* PassepartoutIntents.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE56F02CABE42E0082D21C /* PassepartoutIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 0EDE57002CABE4B50082D21C /* IntentsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE56E72CABE40D0082D21C /* IntentsExtension.swift */; }; - 0EE8D7DD2CD1107E00F6600C /* AppUIMain in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, macos, ); productRef = 0EE8D7DC2CD1107E00F6600C /* AppUIMain */; }; - 0EE8D7DF2CD1108900F6600C /* AppUITV in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = 0EE8D7DE2CD1108900F6600C /* AppUITV */; }; 0EE8D7E12CD112C200F6600C /* App+tvOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE8D7E02CD112C200F6600C /* App+tvOS.swift */; }; /* End PBXBuildFile section */ @@ -145,8 +144,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0EE8D7DF2CD1108900F6600C /* AppUITV in Frameworks */, - 0EE8D7DD2CD1107E00F6600C /* AppUIMain in Frameworks */, + 0EC4A7132CD597EF00B7CAAD /* AppUIPlatform in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -292,8 +290,7 @@ ); name = Passepartout; packageProductDependencies = ( - 0EE8D7DC2CD1107E00F6600C /* AppUIMain */, - 0EE8D7DE2CD1108900F6600C /* AppUITV */, + 0EC4A7122CD597EF00B7CAAD /* AppUIPlatform */, ); productName = PassepartoutKit; productReference = 0E06D18F2B87629100176E1D /* Passepartout.app */; @@ -998,13 +995,9 @@ isa = XCSwiftPackageProductDependency; productName = TunnelLibrary; }; - 0EE8D7DC2CD1107E00F6600C /* AppUIMain */ = { + 0EC4A7122CD597EF00B7CAAD /* AppUIPlatform */ = { isa = XCSwiftPackageProductDependency; - productName = AppUIMain; - }; - 0EE8D7DE2CD1108900F6600C /* AppUITV */ = { - isa = XCSwiftPackageProductDependency; - productName = AppUITV; + productName = AppUIPlatform; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Passepartout/Library/Package.swift b/Passepartout/Library/Package.swift index ffd923ab..c28d81b7 100644 --- a/Passepartout/Library/Package.swift +++ b/Passepartout/Library/Package.swift @@ -25,6 +25,10 @@ let package = Package( name: "AppUIMain", targets: ["AppUIMain"] ), + .library( + name: "AppUIPlatform", + targets: ["AppUIPlatform"] + ), .library( name: "AppUITV", targets: ["AppUITV"] @@ -109,6 +113,13 @@ let package = Package( .process("Resources") ] ), + .target( + name: "AppUIPlatform", + dependencies: [ + .target(name: "AppUIMain", condition: .when(platforms: [.iOS, .macOS])), + .target(name: "AppUITV", condition: .when(platforms: [.tvOS])) + ] + ), .target( name: "AppUITV", dependencies: ["AppUI"] diff --git a/Passepartout/Library/Sources/AppLibrary/Business/ExtendedTunnel.swift b/Passepartout/Library/Sources/AppLibrary/Business/ExtendedTunnel.swift index 8f3051f5..eac8462e 100644 --- a/Passepartout/Library/Sources/AppLibrary/Business/ExtendedTunnel.swift +++ b/Passepartout/Library/Sources/AppLibrary/Business/ExtendedTunnel.swift @@ -84,6 +84,14 @@ public final class ExtendedTunnel: ObservableObject { } .store(in: &subscriptions) + tunnel + .$currentProfile + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.objectWillChange.send() + } + .store(in: &subscriptions) + Timer .publish(every: interval, on: .main, in: .common) .autoconnect() diff --git a/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift b/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift index 5eb9d796..0e2ec97b 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift @@ -297,6 +297,8 @@ public enum Strings { public static let routing = Strings.tr("Localizable", "global.routing", fallback: "Routing") /// Save public static let save = Strings.tr("Localizable", "global.save", fallback: "Save") + /// Select + public static let select = Strings.tr("Localizable", "global.select", fallback: "Select") /// Server public static let server = Strings.tr("Localizable", "global.server", fallback: "Server") /// Servers diff --git a/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings b/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings index 9f79b796..e93d9abd 100644 --- a/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings @@ -62,6 +62,7 @@ "global.routes" = "Routes"; "global.routing" = "Routing"; "global.save" = "Save"; +"global.select" = "Select"; "global.server" = "Server"; "global.servers" = "Servers"; "global.settings" = "Settings"; diff --git a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift index 328cb5df..86270fbc 100644 --- a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift @@ -148,13 +148,13 @@ extension ThemeTappableText { extension ThemeTextField { public var body: some View { - commonView + labeledView } } extension ThemeSecureField { public var body: some View { - commonView + labeledView } } diff --git a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift index a1186ad8..036be9a2 100644 --- a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift @@ -108,15 +108,13 @@ extension ThemeTappableText { extension ThemeTextField { public var body: some View { - commonView - .labelsHidden() + fieldView } } extension ThemeSecureField { public var body: some View { - commonView - .labelsHidden() + fieldView } } diff --git a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift index fff06092..1ee9a437 100644 --- a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift @@ -67,13 +67,13 @@ extension ThemeSectionWithHeaderFooterModifier { extension ThemeTextField { public var body: some View { - commonView + TextField(placeholder, text: $text) } } extension ThemeSecureField { public var body: some View { - commonView + SecureField(placeholder, text: $text) } } diff --git a/Passepartout/Library/Sources/AppUI/Theme/Theme+ImageName.swift b/Passepartout/Library/Sources/AppUI/Theme/Theme+ImageName.swift index 6e8b43f6..120418b5 100644 --- a/Passepartout/Library/Sources/AppUI/Theme/Theme+ImageName.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Theme+ImageName.swift @@ -50,6 +50,7 @@ extension Theme { case profilesGrid case profilesList case remove + case search case settings case share case show @@ -90,6 +91,7 @@ extension Theme.ImageName { case .profilesGrid: return "square.grid.2x2" case .profilesList: return "rectangle.grid.1x2" case .remove: return "minus" + case .search: return "magnifyingglass" case .settings: return "gearshape" case .share: return "square.and.arrow.up" case .show: return "eye" diff --git a/Passepartout/Library/Sources/AppUI/Theme/UI/Theme+Views.swift b/Passepartout/Library/Sources/AppUI/Theme/UI/Theme+Views.swift index 4b93c567..2491b5fc 100644 --- a/Passepartout/Library/Sources/AppUI/Theme/UI/Theme+Views.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/UI/Theme+Views.swift @@ -91,6 +91,25 @@ public struct ThemeImageLabel: View { } } +public struct ThemeCountryText: View { + private let code: String + + private let title: String? + + public init(_ code: String, title: String? = nil) { + self.code = code + self.title = title ?? code.localizedAsRegionCode + } + + public var body: some View { + Text( + [code, title] + .compactMap { $0 } + .joined(separator: " ") + ) + } +} + public struct ThemeCountryFlag: View { private let code: String? @@ -124,12 +143,12 @@ public struct ThemeCountryFlag: View { } public struct ThemeTextField: View { - private let title: String? + let title: String? @Binding - private var text: String + var text: String - private let placeholder: String + let placeholder: String public init(_ title: String, text: Binding, placeholder: String) { self.title = title @@ -138,7 +157,7 @@ public struct ThemeTextField: View { } @ViewBuilder - var commonView: some View { + var labeledView: some View { if let title { LabeledContent { fieldView @@ -150,18 +169,18 @@ public struct ThemeTextField: View { } } - private var fieldView: some View { + var fieldView: some View { TextField(title ?? "", text: $text, prompt: Text(placeholder)) } } public struct ThemeSecureField: View { - private let title: String? + let title: String? @Binding - private var text: String + var text: String - private let placeholder: String + let placeholder: String public init(title: String?, text: Binding, placeholder: String) { self.title = title @@ -170,7 +189,7 @@ public struct ThemeSecureField: View { } @ViewBuilder - var commonView: some View { + var labeledView: some View { if let title { LabeledContent { fieldView @@ -182,7 +201,7 @@ public struct ThemeSecureField: View { } } - private var fieldView: some View { + var fieldView: some View { RevealingSecureField(title ?? "", text: $text, prompt: Text(placeholder), imageWidth: 30.0) { ThemeImage(.hide) .foregroundStyle(Color.accentColor) diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusView.swift b/Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusText.swift similarity index 89% rename from Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusView.swift rename to Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusText.swift index cec6b47c..dd6c4c5f 100644 --- a/Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusView.swift +++ b/Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusText.swift @@ -1,5 +1,5 @@ // -// ConnectionStatusView.swift +// ConnectionStatusText.swift // Passepartout // // Created by Davide De Rosa on 9/4/24. @@ -28,7 +28,7 @@ import Foundation import PassepartoutKit import SwiftUI -public struct ConnectionStatusView: View, ThemeProviding { +public struct ConnectionStatusText: View, ThemeProviding { @EnvironmentObject public var theme: Theme @@ -42,12 +42,10 @@ public struct ConnectionStatusView: View, ThemeProviding { public var body: some View { Text(statusDescription) - .font(.headline) - .foregroundStyle(tunnel.statusColor(theme)) } } -private extension ConnectionStatusView { +private extension ConnectionStatusText { var statusDescription: String { if let lastErrorCode = tunnel.lastErrorCode { return lastErrorCode.localizedDescription @@ -76,7 +74,7 @@ private extension ConnectionStatusView { } #Preview("Connected") { - ConnectionStatusView(tunnel: .mock) + ConnectionStatusText(tunnel: .mock) .task { try? await ExtendedTunnel.mock.connect(with: .mock, processor: .mock) } @@ -95,7 +93,7 @@ private extension ConnectionStatusView { } catch { fatalError() } - return ConnectionStatusView(tunnel: .mock) + return ConnectionStatusText(tunnel: .mock) .task { try? await ExtendedTunnel.mock.connect(with: profile, processor: .mock) } diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/TunnelToggleButton.swift b/Passepartout/Library/Sources/AppUI/Views/UI/TunnelToggleButton.swift index 27512384..b9d11d0d 100644 --- a/Passepartout/Library/Sources/AppUI/Views/UI/TunnelToggleButton.swift +++ b/Passepartout/Library/Sources/AppUI/Views/UI/TunnelToggleButton.swift @@ -29,11 +29,6 @@ import SwiftUI import UtilsLibrary public struct TunnelToggleButton