diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index 1ac0492a..265a1a65 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -23,12 +23,14 @@ 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 */; }; - 0EC9C0232CA5BD0B00C52954 /* AppUI in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC9C0222CA5BD0B00C52954 /* AppUI */; }; - 0ED61CF82CD0418C008FE259 /* AppDelegate+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF72CD0418C008FE259 /* AppDelegate+macOS.swift */; }; - 0ED61CFA2CD04192008FE259 /* AppDelegate+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF92CD04192008FE259 /* AppDelegate+iOS.swift */; }; + 0ED61CF82CD0418C008FE259 /* App+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF72CD0418C008FE259 /* App+macOS.swift */; }; + 0ED61CFA2CD04192008FE259 /* App+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF92CD04192008FE259 /* App+iOS.swift */; }; 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 */ /* Begin PBXContainerItemProxy section */ @@ -115,12 +117,13 @@ 0EC797402B9378E000C093B7 /* Shared+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Shared+App.swift"; sourceTree = ""; }; 0EC797412B9378E000C093B7 /* Shared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = ""; }; 0ED1EFDA2C33059600CBD9BD /* App.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = App.plist; sourceTree = ""; }; - 0ED61CF72CD0418C008FE259 /* AppDelegate+macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+macOS.swift"; sourceTree = ""; }; - 0ED61CF92CD04192008FE259 /* AppDelegate+iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+iOS.swift"; sourceTree = ""; }; + 0ED61CF72CD0418C008FE259 /* App+macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+macOS.swift"; sourceTree = ""; }; + 0ED61CF92CD04192008FE259 /* App+iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+iOS.swift"; sourceTree = ""; }; 0EDE56E52CABE40D0082D21C /* Intents.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Intents.entitlements; sourceTree = ""; }; 0EDE56E62CABE40D0082D21C /* Intents.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Intents.plist; sourceTree = ""; }; 0EDE56E72CABE40D0082D21C /* IntentsExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentsExtension.swift; sourceTree = ""; }; 0EDE56F02CABE42E0082D21C /* PassepartoutIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = PassepartoutIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0EE8D7E02CD112C200F6600C /* App+tvOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+tvOS.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -144,7 +147,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0EC9C0232CA5BD0B00C52954 /* AppUI in Frameworks */, + 0EE8D7DF2CD1108900F6600C /* AppUITV in Frameworks */, + 0EE8D7DD2CD1107E00F6600C /* AppUIMain in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -214,8 +218,7 @@ 0E7E3D5A2B9345FD002BBDB4 /* App */ = { isa = PBXGroup; children = ( - 0ED61CF52CD0416A008FE259 /* iOS */, - 0ED61CF62CD04174008FE259 /* macOS */, + 0ED61CF62CD04174008FE259 /* Platforms */, 0ED1EFDA2C33059600CBD9BD /* App.plist */, 0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */, 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */, @@ -247,20 +250,14 @@ path = Tunnel; sourceTree = ""; }; - 0ED61CF52CD0416A008FE259 /* iOS */ = { + 0ED61CF62CD04174008FE259 /* Platforms */ = { isa = PBXGroup; children = ( - 0ED61CF92CD04192008FE259 /* AppDelegate+iOS.swift */, + 0ED61CF92CD04192008FE259 /* App+iOS.swift */, + 0ED61CF72CD0418C008FE259 /* App+macOS.swift */, + 0EE8D7E02CD112C200F6600C /* App+tvOS.swift */, ); - path = iOS; - sourceTree = ""; - }; - 0ED61CF62CD04174008FE259 /* macOS */ = { - isa = PBXGroup; - children = ( - 0ED61CF72CD0418C008FE259 /* AppDelegate+macOS.swift */, - ); - path = macOS; + path = Platforms; sourceTree = ""; }; 0EDE56E82CABE40D0082D21C /* Intents */ = { @@ -298,7 +295,8 @@ ); name = Passepartout; packageProductDependencies = ( - 0EC9C0222CA5BD0B00C52954 /* AppUI */, + 0EE8D7DC2CD1107E00F6600C /* AppUIMain */, + 0EE8D7DE2CD1108900F6600C /* AppUITV */, ); productName = PassepartoutKit; productReference = 0E06D18F2B87629100176E1D /* Passepartout.app */; @@ -466,11 +464,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0ED61CF82CD0418C008FE259 /* AppDelegate+macOS.swift in Sources */, + 0ED61CF82CD0418C008FE259 /* App+macOS.swift in Sources */, 0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */, - 0ED61CFA2CD04192008FE259 /* AppDelegate+iOS.swift in Sources */, + 0ED61CFA2CD04192008FE259 /* App+iOS.swift in Sources */, 0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */, 0EC797422B9378E000C093B7 /* Shared+App.swift in Sources */, + 0EE8D7E12CD112C200F6600C /* App+tvOS.swift in Sources */, 0EC797432B9378E000C093B7 /* Shared.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -621,7 +620,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3"; TVOS_DEPLOYMENT_TARGET = 17.0; VERSIONING_SYSTEM = "apple-generic"; }; @@ -697,7 +696,7 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3"; TVOS_DEPLOYMENT_TARGET = 17.0; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1001,9 +1000,13 @@ isa = XCSwiftPackageProductDependency; productName = TunnelLibrary; }; - 0EC9C0222CA5BD0B00C52954 /* AppUI */ = { + 0EE8D7DC2CD1107E00F6600C /* AppUIMain */ = { isa = XCSwiftPackageProductDependency; - productName = AppUI; + productName = AppUIMain; + }; + 0EE8D7DE2CD1108900F6600C /* AppUITV */ = { + isa = XCSwiftPackageProductDependency; + productName = AppUITV; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Passepartout/App/PassepartoutApp.swift b/Passepartout/App/PassepartoutApp.swift index 42021dc1..c7801b5b 100644 --- a/Passepartout/App/PassepartoutApp.swift +++ b/Passepartout/App/PassepartoutApp.swift @@ -23,7 +23,12 @@ // along with Passepartout. If not, see . // -import AppUI +#if os(iOS) || os(macOS) +import AppUIMain +#elseif os(tvOS) +import AppUITV +#endif + import CommonLibrary import PassepartoutKit import SwiftUI @@ -31,56 +36,34 @@ import SwiftUI @main struct PassepartoutApp: App { -#if os(iOS) +#if os(iOS) || os(tvOS) + @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate -#else + +#elseif os(macOS) + @NSApplicationDelegateAdaptor private var appDelegate: AppDelegate + #endif @Environment(\.scenePhase) private var scenePhase - private var context: AppContext { + @StateObject + var theme = Theme() +} + +extension PassepartoutApp { + var appName: String { + BundleConfiguration.mainDisplayName + } + + var context: AppContext { appDelegate.context } - private let appName = BundleConfiguration.mainDisplayName - - @StateObject - private var theme = Theme() - -#if os(iOS) - var body: some Scene { - WindowGroup { - contentView() - .onOpenURL { url in - ImporterPipe.shared.send([url]) - } - } - } -#else - var body: some Scene { - Window(appName, id: appName, content: contentView) - .defaultSize(width: 600, height: 400) - - Settings { - SettingsView(profileManager: context.profileManager) - .frame(minWidth: 300, minHeight: 200) - } - MenuBarExtra { - AppMenu() - .withEnvironment(from: context, theme: theme) - } label: { - AppMenuImage(connectionObserver: context.connectionObserver) - .environmentObject(theme) - } - } -#endif -} - -private extension PassepartoutApp { func contentView() -> some View { AppCoordinator( profileManager: context.profileManager, @@ -103,7 +86,6 @@ private extension PassepartoutApp { break } } - .themeLockScreen() .withEnvironment(from: context, theme: theme) } } diff --git a/Passepartout/App/Platforms/App+iOS.swift b/Passepartout/App/Platforms/App+iOS.swift new file mode 100644 index 00000000..ec7f65dc --- /dev/null +++ b/Passepartout/App/Platforms/App+iOS.swift @@ -0,0 +1,50 @@ +// +// App+iOS.swift +// Passepartout +// +// Created by Davide De Rosa on 10/28/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) + +import AppUIMain +import SwiftUI + +extension AppDelegate: UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + configure() + return true + } +} + +extension PassepartoutApp { + var body: some Scene { + WindowGroup { + contentView() + .onOpenURL { url in + ImporterPipe.shared.send([url]) + } + .themeLockScreen() + } + } +} + +#endif diff --git a/Passepartout/App/macOS/AppDelegate+macOS.swift b/Passepartout/App/Platforms/App+macOS.swift similarity index 77% rename from Passepartout/App/macOS/AppDelegate+macOS.swift rename to Passepartout/App/Platforms/App+macOS.swift index 3223ed74..3bc90583 100644 --- a/Passepartout/App/macOS/AppDelegate+macOS.swift +++ b/Passepartout/App/Platforms/App+macOS.swift @@ -1,5 +1,5 @@ // -// AppDelegate+macOS.swift +// App+macOS.swift // Passepartout // // Created by Davide De Rosa on 10/28/24. @@ -25,9 +25,9 @@ #if os(macOS) -import AppKit -import AppUI +import AppUIMain import PassepartoutKit +import SwiftUI extension AppDelegate: NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { @@ -82,4 +82,28 @@ private extension AppDelegate { } } +extension PassepartoutApp { + + @SceneBuilder + var body: some Scene { + Window(appName, id: appName) { + contentView() + .themeLockScreen() + } + .defaultSize(width: 600, height: 400) + + Settings { + SettingsView(profileManager: context.profileManager) + .frame(minWidth: 300, minHeight: 200) + } + MenuBarExtra { + AppMenu() + .withEnvironment(from: context, theme: theme) + } label: { + AppMenuImage(connectionObserver: context.connectionObserver) + .environmentObject(theme) + } + } +} + #endif diff --git a/Passepartout/App/iOS/AppDelegate+iOS.swift b/Passepartout/App/Platforms/App+tvOS.swift similarity index 83% rename from Passepartout/App/iOS/AppDelegate+iOS.swift rename to Passepartout/App/Platforms/App+tvOS.swift index 6ec4342a..158b396d 100644 --- a/Passepartout/App/iOS/AppDelegate+iOS.swift +++ b/Passepartout/App/Platforms/App+tvOS.swift @@ -1,8 +1,8 @@ // -// AppDelegate+iOS.swift +// App+tvOS.swift // Passepartout // -// Created by Davide De Rosa on 10/28/24. +// Created by Davide De Rosa on 10/29/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,10 +23,10 @@ // along with Passepartout. If not, see . // -#if os(iOS) +#if os(tvOS) -import AppUI -import UIKit +import AppUITV +import SwiftUI extension AppDelegate: UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { @@ -35,4 +35,10 @@ extension AppDelegate: UIApplicationDelegate { } } +extension PassepartoutApp { + var body: some Scene { + WindowGroup(content: contentView) + } +} + #endif diff --git a/Passepartout/Library/Package.resolved b/Passepartout/Library/Package.resolved new file mode 100644 index 00000000..187019fc --- /dev/null +++ b/Passepartout/Library/Package.resolved @@ -0,0 +1,85 @@ +{ + "pins" : [ + { + "identity" : "dtfoundation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Cocoanetics/DTFoundation.git", + "state" : { + "revision" : "a61be65dd7d5b2cde3acabd13bf320b71f2907a5", + "version" : "1.7.19" + } + }, + { + "identity" : "generic-json-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/zoul/generic-json-swift", + "state" : { + "revision" : "0a06575f4038b504e78ac330913d920f1630f510", + "version" : "2.0.2" + } + }, + { + "identity" : "kvitto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Cocoanetics/Kvitto", + "state" : { + "revision" : "88888674d772ddcf19671159ed0022cb0bc37be2", + "version" : "1.0.6" + } + }, + { + "identity" : "openssl-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/openssl-apple", + "state" : { + "revision" : "0edc07c7a0e4ec2ca0f448dd68314241ccc925b3", + "version" : "3.2.107" + } + }, + { + "identity" : "passepartoutkit-source", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-source", + "state" : { + "revision" : "31aff403169c7cebe91a07fb8d225ab844a9a9ff" + } + }, + { + "identity" : "passepartoutkit-source-openvpn-openssl", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", + "state" : { + "revision" : "3e687d2348e8e1cbc214e260df73890d6420b4ec", + "version" : "0.9.1" + } + }, + { + "identity" : "passepartoutkit-source-wireguard-go", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-source-wireguard-go", + "state" : { + "revision" : "8d142c806fb7dc4a2cd754d38d99da0d6398b811", + "version" : "0.9.1" + } + }, + { + "identity" : "wg-go-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/wg-go-apple", + "state" : { + "revision" : "860e82efaf261da37483a5f51555be83e5a79ad3", + "version" : "0.0.20240714" + } + }, + { + "identity" : "wireguard-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/wireguard-apple", + "state" : { + "revision" : "a896f784bc5ed94f29d97e376be5cfa08d4a5d44", + "version" : "1.1.1" + } + } + ], + "version" : 2 +} diff --git a/Passepartout/Library/Package.swift b/Passepartout/Library/Package.swift index 12745ed7..64afa4b4 100644 --- a/Passepartout/Library/Package.swift +++ b/Passepartout/Library/Package.swift @@ -21,6 +21,14 @@ let package = Package( name: "AppUI", targets: ["AppUI"] ), + .library( + name: "AppUIMain", + targets: ["AppUIMain"] + ), + .library( + name: "AppUITV", + targets: ["AppUITV"] + ), .library( name: "TunnelLibrary", targets: ["CommonLibrary"] @@ -78,13 +86,26 @@ let package = Package( "AppDataProviders", "AppLibrary", "Kvitto", - "LegacyV2", "UtilsLibrary" ], resources: [ .process("Resources") ] ), + .target( + name: "AppUIMain", + dependencies: [ + "AppUI", + "LegacyV2" + ], + resources: [ + .process("Resources") + ] + ), + .target( + name: "AppUITV", + dependencies: ["AppUI"] + ), .target( name: "CommonLibrary", dependencies: [ @@ -114,6 +135,10 @@ let package = Package( name: "AppLibraryTests", dependencies: ["AppLibrary"] ), + .testTarget( + name: "AppUIMainTests", + dependencies: ["AppUIMain"] + ), .testTarget( name: "AppUITests", dependencies: ["AppUI"] diff --git a/Passepartout/Library/Sources/AppUI/AppUI.swift b/Passepartout/Library/Sources/AppUI/AppUI.swift index 07d5ae04..1f9ff8ac 100644 --- a/Passepartout/Library/Sources/AppUI/AppUI.swift +++ b/Passepartout/Library/Sources/AppUI/AppUI.swift @@ -24,9 +24,12 @@ // import Foundation -import NetworkExtension import PassepartoutKit +public protocol AppUIConfiguring { + static func configure(with context: AppContext) +} + public enum AppUI { public static func configure(with context: AppContext) { assertMissingModuleImplementations() @@ -36,29 +39,13 @@ public enum AppUI { } } -private extension AppUI { - static func assertMissingModuleImplementations() { - let providerModuleTypes: Set = [ - .openVPN - ] +extension AppUI { + public static func assertMissingModuleImplementations() { ModuleType.allCases.forEach { moduleType in let builder = moduleType.newModule() guard builder is ModuleTypeProviding else { fatalError("\(moduleType): is not ModuleTypeProviding") } - guard builder is any ModuleViewProviding else { - fatalError("\(moduleType): is not ModuleViewProviding") - } - if providerModuleTypes.contains(moduleType) { - do { - let module = try builder.tryBuild() - guard module is any ProviderEntityViewProviding else { - fatalError("\(moduleType): is not ProviderEntityViewProviding") - } - } catch { - fatalError("\(moduleType): empty module is not buildable") - } - } } } } diff --git a/Passepartout/Library/Sources/AppUI/Business/ConnectionObserver.swift b/Passepartout/Library/Sources/AppUI/Business/ConnectionObserver.swift index c0b90d46..32c02cc3 100644 --- a/Passepartout/Library/Sources/AppUI/Business/ConnectionObserver.swift +++ b/Passepartout/Library/Sources/AppUI/Business/ConnectionObserver.swift @@ -30,7 +30,7 @@ import PassepartoutKit @MainActor public final class ConnectionObserver: ObservableObject { - let tunnel: Tunnel + public let tunnel: Tunnel private let environment: TunnelEnvironment diff --git a/Passepartout/Library/Sources/AppUI/Business/ProfileEditor.swift b/Passepartout/Library/Sources/AppUI/Business/ProfileEditor.swift index 610ca1fa..63716a10 100644 --- a/Passepartout/Library/Sources/AppUI/Business/ProfileEditor.swift +++ b/Passepartout/Library/Sources/AppUI/Business/ProfileEditor.swift @@ -30,21 +30,21 @@ import Foundation import PassepartoutKit @MainActor -final class ProfileEditor: ObservableObject { +public final class ProfileEditor: ObservableObject { @Published private var editableProfile: EditableProfile @Published - var isShared: Bool + public var isShared: Bool private(set) var removedModules: [UUID: any ModuleBuilder] - convenience init() { + public convenience init() { self.init(modules: []) } - init(modules: [any ModuleBuilder]) { + public init(modules: [any ModuleBuilder]) { editableProfile = EditableProfile( modules: modules, activeModulesIds: Set(modules.map(\.id)) @@ -53,13 +53,13 @@ final class ProfileEditor: ObservableObject { removedModules = [:] } - init(profile: Profile) { + public init(profile: Profile) { editableProfile = profile.editable() isShared = false removedModules = [:] } - func editProfile(_ profile: Profile, isShared: Bool) { + public func editProfile(_ profile: Profile, isShared: Bool) { editableProfile = profile.editable() self.isShared = isShared removedModules = [:] @@ -69,7 +69,7 @@ final class ProfileEditor: ObservableObject { // MARK: - Types extension ProfileEditor { - var moduleTypes: [ModuleType] { + public var moduleTypes: [ModuleType] { editableProfile.modules .compactMap { $0 as? ModuleTypeProviding @@ -77,7 +77,7 @@ extension ProfileEditor { .map(\.moduleType) } - var availableModuleTypes: [ModuleType] { + public var availableModuleTypes: [ModuleType] { ModuleType .allCases .filter { @@ -97,7 +97,7 @@ extension ProfileEditor { // MARK: - Editing extension ProfileEditor { - var profile: EditableProfile { + public var profile: EditableProfile { get { editableProfile } @@ -108,21 +108,21 @@ extension ProfileEditor { } extension ProfileEditor { - var modules: [any ModuleBuilder] { + public var modules: [any ModuleBuilder] { editableProfile.modules } - func module(withId moduleId: UUID) -> (any ModuleBuilder)? { + public func module(withId moduleId: UUID) -> (any ModuleBuilder)? { editableProfile.modules.first { $0.id == moduleId } ?? removedModules[moduleId] } - func isActiveModule(withId moduleId: UUID) -> Bool { + public func isActiveModule(withId moduleId: UUID) -> Bool { editableProfile.isActiveModule(withId: moduleId) } - func toggleModule(withId moduleId: UUID) { + public func toggleModule(withId moduleId: UUID) { guard let existingModule = module(withId: moduleId) else { return } @@ -133,11 +133,11 @@ extension ProfileEditor { } } - func moveModules(from offsets: IndexSet, to newOffset: Int) { + public func moveModules(from offsets: IndexSet, to newOffset: Int) { editableProfile.modules.move(fromOffsets: offsets, toOffset: newOffset) } - func removeModules(at offsets: IndexSet) { + public func removeModules(at offsets: IndexSet) { offsets.forEach { let module = editableProfile.modules[$0] removedModules[module.id] = module @@ -145,7 +145,7 @@ extension ProfileEditor { } } - func removeModule(withId moduleId: UUID) { + public func removeModule(withId moduleId: UUID) { guard let index = editableProfile.modules.firstIndex(where: { $0.id == moduleId }) else { return } @@ -154,7 +154,7 @@ extension ProfileEditor { editableProfile.modules.remove(at: index) } - func saveModule(_ module: any ModuleBuilder, activating: Bool) { + public func saveModule(_ module: any ModuleBuilder, activating: Bool) { if let index = editableProfile.modules.firstIndex(where: { $0.id == module.id }) { editableProfile.modules[index] = module } else { @@ -175,7 +175,7 @@ private extension ProfileEditor { // MARK: - Building extension ProfileEditor { - func build() throws -> Profile { + public func build() throws -> Profile { let builder = try editableProfile.builder() let profile = try builder.tryBuild() @@ -190,7 +190,7 @@ extension ProfileEditor { // MARK: - Saving extension ProfileEditor { - func save(to profileManager: ProfileManager) async throws { + public func save(to profileManager: ProfileManager) async throws { do { let newProfile = try build() try await profileManager.save(newProfile, isShared: isShared) diff --git a/Passepartout/Library/Sources/AppUI/Domain/AppError.swift b/Passepartout/Library/Sources/AppUI/Domain/AppError.swift index bf2c65bb..18aa7067 100644 --- a/Passepartout/Library/Sources/AppUI/Domain/AppError.swift +++ b/Passepartout/Library/Sources/AppUI/Domain/AppError.swift @@ -26,7 +26,7 @@ import Foundation import PassepartoutKit -enum AppError { +public enum AppError { case emptyProfileName case malformedModule(any ModuleBuilder, error: Error) @@ -35,7 +35,7 @@ enum AppError { case generic(PassepartoutError) - init(_ error: Error) { + public init(_ error: Error) { if let spError = error as? AppError { self = spError } else { diff --git a/Passepartout/Library/Sources/AppUI/Domain/EditableProfile.swift b/Passepartout/Library/Sources/AppUI/Domain/EditableProfile.swift index ac848775..610eff09 100644 --- a/Passepartout/Library/Sources/AppUI/Domain/EditableProfile.swift +++ b/Passepartout/Library/Sources/AppUI/Domain/EditableProfile.swift @@ -26,18 +26,18 @@ import Foundation import PassepartoutKit -struct EditableProfile: MutableProfileType { - var id = UUID() +public struct EditableProfile: MutableProfileType { + public var id = UUID() - var name: String = "" + public var name: String = "" - var modules: [any ModuleBuilder] = [] + public var modules: [any ModuleBuilder] = [] - var activeModulesIds: Set = [] + public var activeModulesIds: Set = [] - var modulesMetadata: [UUID: ModuleMetadata]? + public var modulesMetadata: [UUID: ModuleMetadata]? - func builder() throws -> Profile.Builder { + public func builder() throws -> Profile.Builder { var builder = Profile.Builder(id: id) builder.modules = try modules.compactMap { do { @@ -68,7 +68,7 @@ struct EditableProfile: MutableProfileType { } extension Profile { - func editable() -> EditableProfile { + public func editable() -> EditableProfile { EditableProfile( id: id, name: name, @@ -78,7 +78,7 @@ extension Profile { ) } - var modulesBuilders: [any ModuleBuilder] { + public var modulesBuilders: [any ModuleBuilder] { modules.compactMap { guard let buildableModule = $0 as? any BuildableType else { return nil diff --git a/Passepartout/Library/Sources/AppUI/Domain/ModuleType+New.swift b/Passepartout/Library/Sources/AppUI/Domain/ModuleType+New.swift index 79da3c7a..2e8626af 100644 --- a/Passepartout/Library/Sources/AppUI/Domain/ModuleType+New.swift +++ b/Passepartout/Library/Sources/AppUI/Domain/ModuleType+New.swift @@ -27,7 +27,7 @@ import Foundation import PassepartoutKit extension ModuleType { - func newModule() -> any ModuleBuilder { + public func newModule() -> any ModuleBuilder { switch self { case .openVPN: return OpenVPNModule.Builder() diff --git a/Passepartout/Library/Sources/AppUI/Domain/ModuleType.swift b/Passepartout/Library/Sources/AppUI/Domain/ModuleType.swift index 5d59bbaf..3e7428ab 100644 --- a/Passepartout/Library/Sources/AppUI/Domain/ModuleType.swift +++ b/Passepartout/Library/Sources/AppUI/Domain/ModuleType.swift @@ -27,7 +27,7 @@ import Foundation import PassepartoutKit import PassepartoutWireGuardGo -enum ModuleType: String, CaseIterable { +public enum ModuleType: String, CaseIterable { case openVPN case wireGuard @@ -42,7 +42,7 @@ enum ModuleType: String, CaseIterable { } extension ModuleType: Identifiable { - var id: String { + public var id: String { rawValue } } diff --git a/Passepartout/Library/Sources/AppUI/Domain/TunnelInstallation.swift b/Passepartout/Library/Sources/AppUI/Domain/TunnelInstallation.swift index db38ec1c..87756d0a 100644 --- a/Passepartout/Library/Sources/AppUI/Domain/TunnelInstallation.swift +++ b/Passepartout/Library/Sources/AppUI/Domain/TunnelInstallation.swift @@ -26,8 +26,13 @@ import Foundation import PassepartoutKit -struct TunnelInstallation { - let header: ProfileHeader +public struct TunnelInstallation { + public let header: ProfileHeader - let onDemand: Bool + public let onDemand: Bool + + public init(header: ProfileHeader, onDemand: Bool) { + self.header = header + self.onDemand = onDemand + } } diff --git a/Passepartout/Library/Sources/AppUI/Business/Tunnel+Extensions.swift b/Passepartout/Library/Sources/AppUI/Extensions/Tunnel+Extensions.swift similarity index 86% rename from Passepartout/Library/Sources/AppUI/Business/Tunnel+Extensions.swift rename to Passepartout/Library/Sources/AppUI/Extensions/Tunnel+Extensions.swift index 0be17aeb..732bc20c 100644 --- a/Passepartout/Library/Sources/AppUI/Business/Tunnel+Extensions.swift +++ b/Passepartout/Library/Sources/AppUI/Extensions/Tunnel+Extensions.swift @@ -29,17 +29,17 @@ import PassepartoutKit @MainActor extension Tunnel { - func install(_ profile: Profile, processor: ProfileProcessor) async throws { + public func install(_ profile: Profile, processor: ProfileProcessor) async throws { let newProfile = try processor.processed(profile) try await install(newProfile, connect: false, title: processor.title) } - func connect(with profile: Profile, processor: ProfileProcessor) async throws { + public func connect(with profile: Profile, processor: ProfileProcessor) async throws { let newProfile = try processor.processed(profile) try await install(newProfile, connect: true, title: processor.title) } - func currentLog(parameters: Constants.Log) async -> [String] { + public func currentLog(parameters: Constants.Log) async -> [String] { let output = try? await sendMessage(.localLog( sinceLast: parameters.sinceLast, maxLevel: parameters.maxLevel diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/View+Environment.swift b/Passepartout/Library/Sources/AppUI/Extensions/View+Environment.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/View+Environment.swift rename to Passepartout/Library/Sources/AppUI/Extensions/View+Environment.swift diff --git a/Passepartout/Library/Sources/AppUI/IAP/IAPManager.swift b/Passepartout/Library/Sources/AppUI/IAP/IAPManager.swift index 4aa2ce93..77e8a5be 100644 --- a/Passepartout/Library/Sources/AppUI/IAP/IAPManager.swift +++ b/Passepartout/Library/Sources/AppUI/IAP/IAPManager.swift @@ -43,7 +43,7 @@ public final class IAPManager: ObservableObject { private var purchasedAppBuild: Int? - private(set) var purchasedProducts: Set + public private(set) var purchasedProducts: Set private var eligibleFeatures: Set diff --git a/Passepartout/Library/Sources/AppUI/Domain/PaywallReason.swift b/Passepartout/Library/Sources/AppUI/IAP/PaywallReason.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Domain/PaywallReason.swift rename to Passepartout/Library/Sources/AppUI/IAP/PaywallReason.swift diff --git a/Passepartout/Library/Sources/AppUI/L10n/AppError+L10n.swift b/Passepartout/Library/Sources/AppUI/L10n/AppError+L10n.swift index d1513dc6..c31d8824 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/AppError+L10n.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/AppError+L10n.swift @@ -29,7 +29,7 @@ import PassepartoutKit import UtilsLibrary extension AppError: LocalizedError { - var errorDescription: String? { + public var errorDescription: String? { let V = Strings.Errors.App.self switch self { case .emptyProfileName: diff --git a/Passepartout/Library/Sources/AppUI/L10n/EditableModule+Description.swift b/Passepartout/Library/Sources/AppUI/L10n/EditableModule+Description.swift index 5a3904fd..bf6f27d7 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/EditableModule+Description.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/EditableModule+Description.swift @@ -29,13 +29,13 @@ import PassepartoutKit extension ModuleBuilder { @MainActor - func description(inEditor editor: ProfileEditor) -> String { + public func description(inEditor editor: ProfileEditor) -> String { editor.profile.displayName(forModuleWithId: id) ?? typeDescription } } extension ModuleBuilder { - var typeDescription: String { + public var typeDescription: String { guard let providing = self as? ModuleTypeProviding else { return String(describing: self) } diff --git a/Passepartout/Library/Sources/AppUI/L10n/ErrorHandler+Default.swift b/Passepartout/Library/Sources/AppUI/L10n/ErrorHandler+Default.swift index 65461c7e..50a458d1 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/ErrorHandler+Default.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/ErrorHandler+Default.swift @@ -28,7 +28,7 @@ import SwiftUI import UtilsLibrary extension ErrorHandler { - static func `default`() -> ErrorHandler { + public static func `default`() -> ErrorHandler { ErrorHandler( defaultTitle: Strings.Unlocalized.appName, dismissTitle: Strings.Global.ok, diff --git a/Passepartout/Library/Sources/AppUI/L10n/Foundation+L10n.swift b/Passepartout/Library/Sources/AppUI/L10n/Foundation+L10n.swift index 36aeec0c..49101479 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/Foundation+L10n.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/Foundation+L10n.swift @@ -64,7 +64,7 @@ extension Date: StyledLocalizableEntity { } extension UUID { - var flatString: String { + public var flatString: String { let str = uuidString.replacingOccurrences(of: "-", with: "") assert(str.count == 32) return str diff --git a/Passepartout/Library/Sources/AppUI/L10n/Strings+Unlocalized.swift b/Passepartout/Library/Sources/AppUI/L10n/Strings+Unlocalized.swift index 47278919..c0d0bfb3 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/Strings+Unlocalized.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/Strings+Unlocalized.swift @@ -27,9 +27,9 @@ import Foundation import PassepartoutKit extension Strings { - enum Unlocalized { - enum OpenVPN { - enum XOR: String { + public enum Unlocalized { + public enum OpenVPN { + public enum XOR: String { case xormask case xorptrpos @@ -39,23 +39,23 @@ extension Strings { case obfuscate } - static let compLZO = "--comp-lzo" + public static let compLZO = "--comp-lzo" - static let compress = "--compress" + public static let compress = "--compress" - static let lzo = "LZO" + public static let lzo = "LZO" } - enum Placeholders { - static let hostname = "example.com" + public enum Placeholders { + public static let hostname = "example.com" - static let dohURL = "https://1.2.3.4/some-query" + public static let dohURL = "https://1.2.3.4/some-query" - static let dotHostname = "dns-hostname.com" + public static let dotHostname = "dns-hostname.com" - static let ipV4DNS = "1.1.1.1" + public static let ipV4DNS = "1.1.1.1" - static func ipDestination(forFamily family: Address.Family) -> String { + public static func ipDestination(forFamily family: Address.Family) -> String { switch family { case .v4: return "192.168.15.0/24" @@ -65,7 +65,7 @@ extension Strings { } } - static func ipGateway(forFamily family: Address.Family) -> String { + public static func ipGateway(forFamily family: Address.Family) -> String { switch family { case .v4: return "192.168.15.1" @@ -75,67 +75,67 @@ extension Strings { } } - static let mtu = "1500" + public static let mtu = "1500" - static let proxyIPv4Address = "192.168.1.1" + public static let proxyIPv4Address = "192.168.1.1" - static let proxyPort = "1080" + public static let proxyPort = "1080" - static let pacURL = "http://proxy.com/pac.url" + public static let pacURL = "http://proxy.com/pac.url" } - enum Issues { - static let subject = "\(appName) - Report issue" + public enum Issues { + public static let subject = "\(appName) - Report issue" - static let attachmentMimeType = "text/plain" + public static let attachmentMimeType = "text/plain" - static let appLogFilename = "app.log" + public static let appLogFilename = "app.log" - static let tunnelLogFilename = "tunnel.log" + public static let tunnelLogFilename = "tunnel.log" } - static let appName = "Passepartout" + public static let appName = "Passepartout" - static let ca = "CA" + public static let ca = "CA" - static let dns = "DNS" + public static let dns = "DNS" - static let faq = "FAQ" + public static let faq = "FAQ" - static let http = "HTTP" + public static let http = "HTTP" - static let https = "HTTPS" + public static let https = "HTTPS" - static let httpProxy = "HTTP Proxy" + public static let httpProxy = "HTTP Proxy" - static let iCloud = "iCloud" + public static let iCloud = "iCloud" - static let ip = "IP" + public static let ip = "IP" - static let ipv4 = "IPv4" + public static let ipv4 = "IPv4" - static let ipv6 = "IPv6" + public static let ipv6 = "IPv6" - static let mtu = "MTU" + public static let mtu = "MTU" - static let openVPN = "OpenVPN" + public static let openVPN = "OpenVPN" - static let otp = "OTP" + public static let otp = "OTP" - static let pac = "PAC" + public static let pac = "PAC" - static let proxy = "Proxy" + public static let proxy = "Proxy" - static let tls = "TLS" + public static let tls = "TLS" - static let url = "URL" + public static let url = "URL" - static let uuid = "UUID" + public static let uuid = "UUID" - static let wifi = "Wi-Fi" + public static let wifi = "Wi-Fi" - static let wireGuard = "WireGuard" + public static let wireGuard = "WireGuard" - static let xor = "XOR" + public static let xor = "XOR" } } diff --git a/Passepartout/Library/Sources/AppUI/Mock/Mock.swift b/Passepartout/Library/Sources/AppUI/Mock/Mock.swift index b04481b8..1cfb58c4 100644 --- a/Passepartout/Library/Sources/AppUI/Mock/Mock.swift +++ b/Passepartout/Library/Sources/AppUI/Mock/Mock.swift @@ -50,7 +50,7 @@ extension AppContext { profileManager: { let profiles: [Profile] = (0..<20) .reduce(into: []) { list, _ in - list.append(.newProfile()) + list.append(.newMockProfile()) } return ProfileManager(profiles: profiles) }(), @@ -109,7 +109,7 @@ extension ProviderManager { // MARK: - Profile extension Profile { - static let mock: Profile = { + public static let mock: Profile = { var profile = Profile.Builder() profile.name = "Mock profile" @@ -144,7 +144,7 @@ extension Profile { } }() - static func newProfile() -> Profile { + public static func newMockProfile() -> Profile { do { var copy = mock.builder(withNewId: true) copy.name = String(copy.id.uuidString.prefix(8)) diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/TunnelContextProviding.swift b/Passepartout/Library/Sources/AppUI/Protocols/TunnelContextProviding.swift similarity index 93% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/TunnelContextProviding.swift rename to Passepartout/Library/Sources/AppUI/Protocols/TunnelContextProviding.swift index c63feb4b..cffe0f16 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Extensions/TunnelContextProviding.swift +++ b/Passepartout/Library/Sources/AppUI/Protocols/TunnelContextProviding.swift @@ -26,13 +26,13 @@ import Foundation import PassepartoutKit -protocol TunnelContextProviding { +public protocol TunnelContextProviding { var connectionObserver: ConnectionObserver { get } } @MainActor extension TunnelContextProviding { - var tunnelConnectionStatus: TunnelStatus { + public var tunnelConnectionStatus: TunnelStatus { var status = connectionObserver.tunnel.status if status == .active, let connectionStatus = connectionObserver.connectionStatus { if connectionStatus == .connected { diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/TunnelInstallationProviding.swift b/Passepartout/Library/Sources/AppUI/Protocols/TunnelInstallationProviding.swift similarity index 91% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/TunnelInstallationProviding.swift rename to Passepartout/Library/Sources/AppUI/Protocols/TunnelInstallationProviding.swift index 8bbe8677..7fde139e 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Extensions/TunnelInstallationProviding.swift +++ b/Passepartout/Library/Sources/AppUI/Protocols/TunnelInstallationProviding.swift @@ -27,7 +27,7 @@ import AppLibrary import Foundation import PassepartoutKit -protocol TunnelInstallationProviding { +public protocol TunnelInstallationProviding { var profileManager: ProfileManager { get } var tunnel: Tunnel { get } @@ -35,7 +35,7 @@ protocol TunnelInstallationProviding { @MainActor extension TunnelInstallationProviding { - var installation: TunnelInstallation? { + public var installation: TunnelInstallation? { guard let currentProfile = tunnel.currentProfile else { return nil } @@ -47,7 +47,7 @@ extension TunnelInstallationProviding { return TunnelInstallation(header: header, onDemand: currentProfile.onDemand) } - var currentProfile: Profile? { + public var currentProfile: Profile? { guard let id = tunnel.currentProfile?.id else { return nil } diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+iOS.swift b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift similarity index 94% rename from Passepartout/Library/Sources/AppUI/Views/Theme/Theme+iOS.swift rename to Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift index 6981a957..99650a85 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+iOS.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+iOS.swift @@ -84,20 +84,20 @@ extension ThemeSectionWithHeaderFooterModifier { // MARK: - Views extension ThemeTappableText { - var body: some View { + public var body: some View { commonView .foregroundStyle(.primary) } } extension ThemeTextField { - var body: some View { + public var body: some View { commonView } } extension ThemeSecureField { - var body: some View { + public var body: some View { commonView } } @@ -109,13 +109,13 @@ extension ThemeRemovableItemRow { } extension ThemeEditableListSection.RemoveLabel { - var body: some View { + public var body: some View { EmptyView() } } extension ThemeEditableListSection.EditLabel { - var body: some View { + public var body: some View { EmptyView() } } diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+macOS.swift b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift similarity index 95% rename from Passepartout/Library/Sources/AppUI/Views/Theme/Theme+macOS.swift rename to Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift index 81b7028e..57a74e2e 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+macOS.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+macOS.swift @@ -91,7 +91,7 @@ extension ThemeSectionWithHeaderFooterModifier { // MARK: - Views extension ThemeTappableText { - var body: some View { + public var body: some View { commonView .buttonStyle(.plain) .cursor(.hand) @@ -99,14 +99,14 @@ extension ThemeTappableText { } extension ThemeTextField { - var body: some View { + public var body: some View { commonView .labelsHidden() } } extension ThemeSecureField { - var body: some View { + public var body: some View { commonView .labelsHidden() } @@ -122,7 +122,7 @@ extension ThemeRemovableItemRow { } extension ThemeEditableListSection.RemoveLabel { - var body: some View { + public var body: some View { Button(action: action) { ThemeImage(.editableSectionRemove) } @@ -131,7 +131,7 @@ extension ThemeEditableListSection.RemoveLabel { } extension ThemeEditableListSection.EditLabel { - var body: some View { + public var body: some View { ThemeImage(.editableSectionEdit) } } diff --git a/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift new file mode 100644 index 00000000..8bdee039 --- /dev/null +++ b/Passepartout/Library/Sources/AppUI/Theme/Platforms/Theme+tvOS.swift @@ -0,0 +1,36 @@ +// +// Theme+tvOS.swift +// Passepartout +// +// Created by Davide De Rosa on 10/29/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(tvOS) + +import SwiftUI + +extension Theme { + public convenience init() { + self.init(dummy: Void()) + } +} + +#endif diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+ImageName.swift b/Passepartout/Library/Sources/AppUI/Theme/Theme+ImageName.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Theme/Theme+ImageName.swift rename to Passepartout/Library/Sources/AppUI/Theme/Theme+ImageName.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+MenuImageName.swift b/Passepartout/Library/Sources/AppUI/Theme/Theme+MenuImageName.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Theme/Theme+MenuImageName.swift rename to Passepartout/Library/Sources/AppUI/Theme/Theme+MenuImageName.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift b/Passepartout/Library/Sources/AppUI/Theme/Theme+UI.swift similarity index 76% rename from Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift rename to Passepartout/Library/Sources/AppUI/Theme/Theme+UI.swift index bb580eb9..6e46cdae 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Theme+UI.swift @@ -24,12 +24,16 @@ // import CommonLibrary +#if canImport(LocalAuthentication) import LocalAuthentication +#endif import SwiftUI import UtilsLibrary // MARK: - Modifiers +#if !os(tvOS) + struct ThemeWindowModifier: ViewModifier { let size: CGSize } @@ -381,6 +385,8 @@ struct ThemeTipModifier: ViewModifier { } } +#endif + // MARK: - Views public enum ThemeAnimationCategory: CaseIterable { @@ -395,23 +401,23 @@ public enum ThemeAnimationCategory: CaseIterable { case providers } -struct ThemeImage: View { +public struct ThemeImage: View { @EnvironmentObject private var theme: Theme private let name: Theme.ImageName - init(_ name: Theme.ImageName) { + public init(_ name: Theme.ImageName) { self.name = name } - var body: some View { + public var body: some View { Image(systemName: theme.systemImageName(name)) } } -struct ThemeImageLabel: View { +public struct ThemeImageLabel: View { @EnvironmentObject private var theme: Theme @@ -420,12 +426,12 @@ struct ThemeImageLabel: View { private let name: Theme.ImageName - init(_ title: String, _ name: Theme.ImageName) { + public init(_ title: String, _ name: Theme.ImageName) { self.title = title self.name = name } - var body: some View { + public var body: some View { Label { Text(title) } icon: { @@ -434,34 +440,80 @@ struct ThemeImageLabel: View { } } -struct ThemeMenuImage: View { +public struct ThemeCountryFlag: View { + private let code: String? + + private let placeholderTip: String? + + private let countryTip: ((String) -> String?)? + + public init(code: String?, placeholderTip: String? = nil, countryTip: ((String) -> String?)? = nil) { + self.code = code + self.placeholderTip = placeholderTip + self.countryTip = countryTip + } + + public var body: some View { + Group { + if let code { + let image = Image("flags/\(code.lowercased())") + .resizable() + + if let tip = countryTip?(code) { + image + .help(tip) + } else { + image + } + } else { + let image = Image(systemName: "globe") + if let placeholderTip { + image + .help(placeholderTip) + } else { + image + } + } + } + .frame(width: 20, height: 15) + } +} + +#if !os(tvOS) + +public struct ThemeMenuImage: View { @EnvironmentObject private var theme: Theme private let name: Theme.MenuImageName - init(_ name: Theme.MenuImageName) { + public init(_ name: Theme.MenuImageName) { self.name = name } - var body: some View { + public var body: some View { Image(theme.menuImageName(name)) } } -struct ThemeDisclosableMenu: View where Content: View, Label: View { +public struct ThemeDisclosableMenu: View where Content: View, Label: View { @ViewBuilder - let content: () -> Content + private let content: () -> Content @ViewBuilder - let label: Label + private let label: () -> Label - var body: some View { + public init(content: @escaping () -> Content, label: @escaping () -> Label) { + self.content = content + self.label = label + } + + public var body: some View { Menu(content: content) { HStack(alignment: .firstTextBaseline) { - label + label() ThemeImage(.disclose) } .contentShape(.rect) @@ -473,20 +525,32 @@ struct ThemeDisclosableMenu: View where Content: View, Label: Vi } } -struct ThemeCopiableText: View where Value: CustomStringConvertible, ValueView: View { +public struct ThemeCopiableText: View where Value: CustomStringConvertible, ValueView: View { @EnvironmentObject private var theme: Theme - var title: String? + private let title: String? - let value: Value + private let value: Value - var isMultiLine = true + private let isMultiLine: Bool - let valueView: (Value) -> ValueView + private let valueView: (Value) -> ValueView - var body: some View { + public init( + title: String? = nil, + value: Value, + isMultiLine: Bool = true, + valueView: @escaping (Value) -> ValueView + ) { + self.title = title + self.value = value + self.isMultiLine = isMultiLine + self.valueView = valueView + } + + public var body: some View { HStack { if let title { Text(title) @@ -509,10 +573,15 @@ struct ThemeCopiableText: View where Value: CustomStringConver } } -struct ThemeTappableText: View { - let title: String +public struct ThemeTappableText: View { + private let title: String - let action: () -> Void + private let action: () -> Void + + public init(title: String, action: @escaping () -> Void) { + self.title = title + self.action = action + } var commonView: some View { Button(action: action) { @@ -522,15 +591,15 @@ struct ThemeTappableText: View { } } -struct ThemeTextField: View { - let title: String? +public struct ThemeTextField: View { + private let title: String? @Binding - var text: String + private var text: String - let placeholder: String + private let placeholder: String - init(_ title: String, text: Binding, placeholder: String) { + public init(_ title: String, text: Binding, placeholder: String) { self.title = title _text = text self.placeholder = placeholder @@ -554,13 +623,19 @@ struct ThemeTextField: View { } } -struct ThemeSecureField: View { - let title: String? +public struct ThemeSecureField: View { + private let title: String? @Binding - var text: String + private var text: String - let placeholder: String + private let placeholder: String + + public init(title: String?, text: Binding, placeholder: String) { + self.title = title + _text = text + self.placeholder = placeholder + } @ViewBuilder var commonView: some View { @@ -586,15 +661,25 @@ struct ThemeSecureField: View { } } -struct ThemeRemovableItemRow: View where ItemView: View { - let isEditing: Bool +public struct ThemeRemovableItemRow: View where ItemView: View { + private let isEditing: Bool @ViewBuilder - let itemView: () -> ItemView + private let itemView: () -> ItemView let removeAction: () -> Void - var body: some View { + public init( + isEditing: Bool, + @ViewBuilder itemView: @escaping () -> ItemView, + removeAction: @escaping () -> Void + ) { + self.isEditing = isEditing + self.itemView = itemView + self.removeAction = removeAction + } + + public var body: some View { RemovableItemRow( isEditing: isEditing, itemView: itemView, @@ -603,11 +688,17 @@ struct ThemeRemovableItemRow: View where ItemView: View { } } -enum ThemeEditableListSection { - struct RemoveLabel: View { +public enum ThemeEditableListSection { + public struct RemoveLabel: View { let action: () -> Void + + public init(action: @escaping () -> Void) { + self.action = action + } } - struct EditLabel: View { + public struct EditLabel: View { } } + +#endif diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift b/Passepartout/Library/Sources/AppUI/Theme/Theme.swift similarity index 73% rename from Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift rename to Passepartout/Library/Sources/AppUI/Theme/Theme.swift index b43d6c6a..18b59ebc 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/Theme.swift @@ -28,68 +28,62 @@ import UtilsLibrary @MainActor public final class Theme: ObservableObject { + public internal(set) var rootModalSize: CGSize? -// @Published -// private var palette: Palette -// -// public init(palette: Palette) { -// self.palette = palette -// } + public internal(set) var secondaryModalSize: CGSize? - var rootModalSize: CGSize? + public internal(set) var popoverSize: CGSize? - var secondaryModalSize: CGSize? + public internal(set) var relevantWeight: Font.Weight = .semibold - var popoverSize: CGSize? + public internal(set) var titleColor: Color = .primary - var relevantWeight: Font.Weight = .semibold + public internal(set) var valueColor: Color = .secondary - var titleColor: Color = .primary + public internal(set) var gridHeaderStyle: Font = .headline - var valueColor: Color = .secondary + public internal(set) var gridRadius: CGFloat = 12.0 - var gridHeaderStyle: Font = .headline + public internal(set) var gridHeaderBottom: CGFloat = 8.0 - var gridRadius: CGFloat = 12.0 + public internal(set) var gridCellColor: HierarchicalShapeStyle = .quinary - var gridHeaderBottom: CGFloat = 8.0 + public internal(set) var gridCellActiveColor: HierarchicalShapeStyle = .quaternary - var gridCellColor: HierarchicalShapeStyle = .quinary + public internal(set) var emptyMessageFont: Font = .title - var gridCellActiveColor: HierarchicalShapeStyle = .quaternary + public internal(set) var emptyMessageColor: Color = .secondary - var emptyMessageFont: Font = .title + public internal(set) var primaryColor = Color(red: 0.318, green: 0.365, blue: 0.443) - var emptyMessageColor: Color = .secondary + public internal(set) var activeColor = Color(red: .zero, green: Double(0xAA) / 255.0, blue: .zero) - var primaryColor = Color(red: 0.318, green: 0.365, blue: 0.443) + public internal(set) var inactiveColor: Color = .gray - var activeColor = Color(red: .zero, green: Double(0xAA) / 255.0, blue: .zero) + public internal(set) var pendingColor: Color = .orange - var inactiveColor: Color = .gray - - var pendingColor: Color = .orange - - var errorColor: Color = .red + public internal(set) var errorColor: Color = .red private var animation: Animation = .spring - var animationCategories: Set = Set(ThemeAnimationCategory.allCases) + public internal(set) var animationCategories: Set = Set(ThemeAnimationCategory.allCases) - var logoImage = "Logo" + public internal(set) var logoImage = "Logo" - var systemImageName: (ImageName) -> String = Theme.ImageName.defaultSystemName + public internal(set) var systemImageName: (ImageName) -> String = Theme.ImageName.defaultSystemName - var menuImageName: (MenuImageName) -> String = Theme.MenuImageName.defaultImageName + public internal(set) var menuImageName: (MenuImageName) -> String = Theme.MenuImageName.defaultImageName init(dummy: Void) { } - func animation(for category: ThemeAnimationCategory) -> Animation? { + public func animation(for category: ThemeAnimationCategory) -> Animation? { animationCategories.contains(category) ? animation : nil } } +#if !os(tvOS) + // MARK: - Modifiers extension View { @@ -214,39 +208,6 @@ extension View { } } -struct ThemeCountryFlag: View { - let code: String? - - var placeholderTip: String? - - var countryTip: ((String) -> String?)? - - var body: some View { - Group { - if let code { - let image = Image("flags/\(code.lowercased())") - .resizable() - - if let tip = countryTip?(code) { - image - .help(tip) - } else { - image - } - } else { - let image = Image(systemName: "globe") - if let placeholderTip { - image - .help(placeholderTip) - } else { - image - } - } - } - .frame(width: 20, height: 15) - } -} - // MARK: - Views extension Theme { @@ -268,3 +229,5 @@ extension Theme { ) } } + +#endif diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/ThemeProviding.swift b/Passepartout/Library/Sources/AppUI/Theme/ThemeProviding.swift similarity index 96% rename from Passepartout/Library/Sources/AppUI/Views/Theme/ThemeProviding.swift rename to Passepartout/Library/Sources/AppUI/Theme/ThemeProviding.swift index 61a0caea..e73d59bd 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/ThemeProviding.swift +++ b/Passepartout/Library/Sources/AppUI/Theme/ThemeProviding.swift @@ -25,6 +25,6 @@ import Foundation -protocol ThemeProviding { +public protocol ThemeProviding { var theme: Theme { get } } diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/LogoView.swift b/Passepartout/Library/Sources/AppUI/UI/LogoView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/LogoView.swift rename to Passepartout/Library/Sources/AppUI/UI/LogoView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ProfileEditor+UI.swift b/Passepartout/Library/Sources/AppUI/UI/ProfileEditor+UI.swift similarity index 86% rename from Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ProfileEditor+UI.swift rename to Passepartout/Library/Sources/AppUI/UI/ProfileEditor+UI.swift index cc0da83f..e74490e0 100644 --- a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ProfileEditor+UI.swift +++ b/Passepartout/Library/Sources/AppUI/UI/ProfileEditor+UI.swift @@ -27,7 +27,7 @@ import PassepartoutKit import SwiftUI extension ProfileEditor { - func binding(forNameOf moduleId: UUID) -> Binding { + public func binding(forNameOf moduleId: UUID) -> Binding { Binding { [weak self] in self?.profile.name(forModuleWithId: moduleId) ?? "" } set: { [weak self] in @@ -35,7 +35,7 @@ extension ProfileEditor { } } - func binding(forProviderOf moduleId: UUID) -> Binding { + public func binding(forProviderOf moduleId: UUID) -> Binding { Binding { [weak self] in self?.profile.providerId(forModuleWithId: moduleId) } set: { [weak self] in @@ -43,7 +43,7 @@ extension ProfileEditor { } } - func binding(forProviderEntityOf moduleId: UUID) -> Binding where E: ProviderEntity & Codable { + public func binding(forProviderEntityOf moduleId: UUID) -> Binding where E: ProviderEntity & Codable { Binding { [weak self] in try? self?.profile.providerEntity(E.self, forModuleWithId: moduleId) } set: { [weak self] in @@ -51,7 +51,7 @@ extension ProfileEditor { } } - subscript(module: T) -> Binding where T: ModuleBuilder { + public subscript(module: T) -> Binding where T: ModuleBuilder { Binding { [weak self] in guard let foundModule = self?.module(withId: module.id) else { fatalError("Module not found in editor: \(module.id)") diff --git a/Passepartout/Library/Sources/AppUIMain/AppUIMain.swift b/Passepartout/Library/Sources/AppUIMain/AppUIMain.swift new file mode 100644 index 00000000..5b511919 --- /dev/null +++ b/Passepartout/Library/Sources/AppUIMain/AppUIMain.swift @@ -0,0 +1,58 @@ +// +// AppUIMain.swift +// Passepartout +// +// Created by Davide De Rosa on 10/29/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +@_exported import AppUI +import Foundation + +public enum AppUIMain: AppUIConfiguring { + public static func configure(with context: AppContext) { + assertMissingModuleImplementations() + AppUI.configure(with: context) + } +} + +private extension AppUIMain { + static func assertMissingModuleImplementations() { + let providerModuleTypes: Set = [ + .openVPN + ] + ModuleType.allCases.forEach { moduleType in + let builder = moduleType.newModule() + guard builder is any ModuleViewProviding else { + fatalError("\(moduleType): is not ModuleViewProviding") + } + if providerModuleTypes.contains(moduleType) { + do { + let module = try builder.tryBuild() + guard module is any ProviderEntityViewProviding else { + fatalError("\(moduleType): is not ProviderEntityViewProviding") + } + } catch { + fatalError("\(moduleType): empty module is not buildable") + } + } + } + } +} diff --git a/Passepartout/Library/Sources/AppUI/Business/ImporterPipe.swift b/Passepartout/Library/Sources/AppUIMain/Business/ImporterPipe.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Business/ImporterPipe.swift rename to Passepartout/Library/Sources/AppUIMain/Business/ImporterPipe.swift diff --git a/Passepartout/Library/Sources/AppUI/Business/InteractiveManager.swift b/Passepartout/Library/Sources/AppUIMain/Business/InteractiveManager.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Business/InteractiveManager.swift rename to Passepartout/Library/Sources/AppUIMain/Business/InteractiveManager.swift diff --git a/Passepartout/Library/Sources/AppUI/Business/ProfileImporter.swift b/Passepartout/Library/Sources/AppUIMain/Business/ProfileImporter.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Business/ProfileImporter.swift rename to Passepartout/Library/Sources/AppUIMain/Business/ProfileImporter.swift diff --git a/Passepartout/Library/Sources/AppUI/Business/ProviderFavoritesManager.swift b/Passepartout/Library/Sources/AppUIMain/Business/ProviderFavoritesManager.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Business/ProviderFavoritesManager.swift rename to Passepartout/Library/Sources/AppUIMain/Business/ProviderFavoritesManager.swift diff --git a/Passepartout/Library/Sources/AppUI/Domain/Issue+Metadata.swift b/Passepartout/Library/Sources/AppUIMain/Domain/Issue+Metadata.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Domain/Issue+Metadata.swift rename to Passepartout/Library/Sources/AppUIMain/Domain/Issue+Metadata.swift diff --git a/Passepartout/Library/Sources/AppUI/Domain/Issue.swift b/Passepartout/Library/Sources/AppUIMain/Domain/Issue.swift similarity index 99% rename from Passepartout/Library/Sources/AppUI/Domain/Issue.swift rename to Passepartout/Library/Sources/AppUIMain/Domain/Issue.swift index a2c8cb0e..ddb33e21 100644 --- a/Passepartout/Library/Sources/AppUI/Domain/Issue.swift +++ b/Passepartout/Library/Sources/AppUIMain/Domain/Issue.swift @@ -24,15 +24,19 @@ // #if os(iOS) + import CommonLibrary import Foundation import PassepartoutKit import UIKit + #else + import AppKit import CommonLibrary import Foundation import PassepartoutKit + #endif struct Issue: Identifiable { diff --git a/Passepartout/Library/Sources/AppUI/Domain/ProfilesLayout.swift b/Passepartout/Library/Sources/AppUIMain/Domain/ProfilesLayout.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Domain/ProfilesLayout.swift rename to Passepartout/Library/Sources/AppUIMain/Domain/ProfilesLayout.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/EnvironmentValues+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Extensions/EnvironmentValues+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/EnvironmentValues+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Extensions/EnvironmentValues+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Business/ProfileManager+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Extensions/ProfileManager+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Business/ProfileManager+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Extensions/ProfileManager+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/ProviderEntityViewProviding.swift b/Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift similarity index 81% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/ProviderEntityViewProviding.swift rename to Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift index a7bad5bb..6b5df32a 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Extensions/ProviderEntityViewProviding.swift +++ b/Passepartout/Library/Sources/AppUIMain/Extensions/ProviderEntityViewProviding+Extensions.swift @@ -1,8 +1,8 @@ // -// ProviderEntityViewProviding.swift +// ProviderEntityViewProviding+Extensions.swift // Passepartout // -// Created by Davide De Rosa on 10/16/24. +// Created by Davide De Rosa on 10/29/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -26,16 +26,6 @@ import PassepartoutKit import SwiftUI -protocol ProviderEntityViewProviding { - associatedtype EntityContent: View - - @MainActor - func providerEntityView( - with provider: ModuleMetadata.Provider, - onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void - ) -> EntityContent -} - extension ProviderEntityViewProviding where Self: ProviderCompatibleModule, EntityType.Configuration: ProviderConfigurationIdentifiable & Codable { func vpnProviderEntityView( with provider: ModuleMetadata.Provider, diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/InteractiveViewProviding.swift b/Passepartout/Library/Sources/AppUIMain/Protocols/InteractiveViewProviding.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/InteractiveViewProviding.swift rename to Passepartout/Library/Sources/AppUIMain/Protocols/InteractiveViewProviding.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/ModuleDraftEditing.swift b/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleDraftEditing.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/ModuleDraftEditing.swift rename to Passepartout/Library/Sources/AppUIMain/Protocols/ModuleDraftEditing.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleViewFactory.swift b/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewFactory.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleViewFactory.swift rename to Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewFactory.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Extensions/ModuleViewProviding.swift b/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewProviding.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Extensions/ModuleViewProviding.swift rename to Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewProviding.swift diff --git a/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift b/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift new file mode 100644 index 00000000..6b6dd996 --- /dev/null +++ b/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift @@ -0,0 +1,37 @@ +// +// ProviderEntityViewProviding.swift +// Passepartout +// +// Created by Davide De Rosa on 10/16/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +protocol ProviderEntityViewProviding { + associatedtype EntityContent: View + + @MainActor + func providerEntityView( + with provider: ModuleMetadata.Provider, + onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void + ) -> EntityContent +} diff --git a/Passepartout/Library/Sources/AppUI/Resources/Credits.json b/Passepartout/Library/Sources/AppUIMain/Resources/Credits.json similarity index 100% rename from Passepartout/Library/Sources/AppUI/Resources/Credits.json rename to Passepartout/Library/Sources/AppUIMain/Resources/Credits.json diff --git a/Passepartout/Library/Sources/AppUI/Resources/Issue.txt b/Passepartout/Library/Sources/AppUIMain/Resources/Issue.txt similarity index 100% rename from Passepartout/Library/Sources/AppUI/Resources/Issue.txt rename to Passepartout/Library/Sources/AppUIMain/Resources/Issue.txt diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/AddProfileMenu.swift b/Passepartout/Library/Sources/AppUIMain/UI/AddProfileMenu.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/AddProfileMenu.swift rename to Passepartout/Library/Sources/AppUIMain/UI/AddProfileMenu.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusView.swift b/Passepartout/Library/Sources/AppUIMain/UI/ConnectionStatusView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ConnectionStatusView.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ConnectionStatusView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/DefaultModuleViewFactory.swift b/Passepartout/Library/Sources/AppUIMain/UI/DefaultModuleViewFactory.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/ProfileEditor/DefaultModuleViewFactory.swift rename to Passepartout/Library/Sources/AppUIMain/UI/DefaultModuleViewFactory.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/EditableModule+Previews.swift b/Passepartout/Library/Sources/AppUIMain/UI/EditableModule+Previews.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/ProfileEditor/EditableModule+Previews.swift rename to Passepartout/Library/Sources/AppUIMain/UI/EditableModule+Previews.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/EditorModuleToggle.swift b/Passepartout/Library/Sources/AppUIMain/UI/EditorModuleToggle.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/EditorModuleToggle.swift rename to Passepartout/Library/Sources/AppUIMain/UI/EditorModuleToggle.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/FavoriteToggle.swift b/Passepartout/Library/Sources/AppUIMain/UI/FavoriteToggle.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/FavoriteToggle.swift rename to Passepartout/Library/Sources/AppUIMain/UI/FavoriteToggle.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/InstalledProfileView.swift b/Passepartout/Library/Sources/AppUIMain/UI/InstalledProfileView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/InstalledProfileView.swift rename to Passepartout/Library/Sources/AppUIMain/UI/InstalledProfileView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/InteractiveView.swift b/Passepartout/Library/Sources/AppUIMain/UI/InteractiveView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/InteractiveView.swift rename to Passepartout/Library/Sources/AppUIMain/UI/InteractiveView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleSection.swift b/Passepartout/Library/Sources/AppUIMain/UI/ModuleSection.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleSection.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ModuleSection.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleViewModifier.swift b/Passepartout/Library/Sources/AppUIMain/UI/ModuleViewModifier.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleViewModifier.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ModuleViewModifier.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/NameSection.swift b/Passepartout/Library/Sources/AppUIMain/UI/NameSection.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/NameSection.swift rename to Passepartout/Library/Sources/AppUIMain/UI/NameSection.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileCardView.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileCardView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileCardView.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileCardView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileContextMenu.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileContextMenu.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileContextMenu.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileContextMenu.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileDuplicateButton.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileDuplicateButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileDuplicateButton.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileDuplicateButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileFlow.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileFlow.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileFlow.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileFlow.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileInfoButton.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileInfoButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileInfoButton.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileInfoButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileRemoveButton.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileRemoveButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileRemoveButton.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileRemoveButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileRowView.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileRowView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileRowView.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileRowView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfileSaveButton.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfileSaveButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfileSaveButton.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfileSaveButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/ProfilesLayoutPicker.swift b/Passepartout/Library/Sources/AppUIMain/UI/ProfilesLayoutPicker.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/ProfilesLayoutPicker.swift rename to Passepartout/Library/Sources/AppUIMain/UI/ProfilesLayoutPicker.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/StorageSection.swift b/Passepartout/Library/Sources/AppUIMain/UI/StorageSection.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/StorageSection.swift rename to Passepartout/Library/Sources/AppUIMain/UI/StorageSection.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/TunnelContextProviding+Theme.swift b/Passepartout/Library/Sources/AppUIMain/UI/TunnelContextProviding+Theme.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/TunnelContextProviding+Theme.swift rename to Passepartout/Library/Sources/AppUIMain/UI/TunnelContextProviding+Theme.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/TunnelRestartButton.swift b/Passepartout/Library/Sources/AppUIMain/UI/TunnelRestartButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/TunnelRestartButton.swift rename to Passepartout/Library/Sources/AppUIMain/UI/TunnelRestartButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/TunnelToggleButton.swift b/Passepartout/Library/Sources/AppUIMain/UI/TunnelToggleButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/UI/TunnelToggleButton.swift rename to Passepartout/Library/Sources/AppUIMain/UI/TunnelToggleButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/AboutRouterView.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/AboutRouterView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/AboutRouterView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/AboutRouterView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/AboutView.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/AboutView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/AboutView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/AboutView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/CreditsView.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/CreditsView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/CreditsView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/CreditsView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/DonateView.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/DonateView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/DonateView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/DonateView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/LinksView.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/LinksView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/LinksView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/LinksView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutRouterView+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/iOS/AboutRouterView+iOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutRouterView+iOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/iOS/AboutRouterView+iOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutView+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/iOS/AboutView+iOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutView+iOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/iOS/AboutView+iOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutRouterView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/macOS/AboutRouterView+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutRouterView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/macOS/AboutRouterView+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/About/macOS/AboutView+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/About/macOS/AboutView+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift similarity index 94% rename from Passepartout/Library/Sources/AppUI/Views/App/AppCoordinator.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift index 00044aa4..f0e86f7b 100644 --- a/Passepartout/Library/Sources/AppUI/Views/App/AppCoordinator.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/App/AppCoordinator.swift @@ -39,11 +39,11 @@ public struct AppCoordinator: View { @AppStorage(AppPreference.profilesLayout.key) private var layout: ProfilesLayout = .list - let profileManager: ProfileManager + private let profileManager: ProfileManager - let tunnel: Tunnel + private let tunnel: Tunnel - let registry: Registry + private let registry: Registry @StateObject private var profileEditor = ProfileEditor() diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppInlineCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/AppInlineCoordinator.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/AppInlineCoordinator.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/AppInlineCoordinator.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppModalCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/AppModalCoordinator.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/AppModalCoordinator.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/AppModalCoordinator.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppToolbar.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/AppToolbar.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/AppToolbar.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/AppToolbar.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/ProfileContainerView.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContainerView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/ProfileContainerView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/ProfileContainerView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/ProfileGridView.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileGridView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/ProfileGridView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/ProfileGridView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/ProfileImporterModifier.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileImporterModifier.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/ProfileImporterModifier.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/ProfileImporterModifier.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/ProfileListView.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProfileListView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/ProfileListView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/ProfileListView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/App/ProviderEntitySelector.swift b/Passepartout/Library/Sources/AppUIMain/Views/App/ProviderEntitySelector.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/App/ProviderEntitySelector.swift rename to Passepartout/Library/Sources/AppUIMain/Views/App/ProviderEntitySelector.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenu+Model.swift b/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenu+Model.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenu+Model.swift rename to Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenu+Model.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenu.swift b/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenu.swift similarity index 97% rename from Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenu.swift rename to Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenu.swift index 3c6bdedc..bd30e750 100644 --- a/Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenu.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenu.swift @@ -75,7 +75,7 @@ private extension AppMenu { } var profilesList: some View { - ForEach(profileManager.headers, id: \.self, content: profileToggle) + ForEach(profileManager.headers, id: \.id, content: profileToggle) } func profileToggle(for header: ProfileHeader) -> some View { diff --git a/Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenuImage.swift b/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenuImage.swift similarity index 96% rename from Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenuImage.swift rename to Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenuImage.swift index 21ff1596..03690964 100644 --- a/Passepartout/Library/Sources/AppUI/Views/AppMenu/AppMenuImage.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppMenuImage.swift @@ -31,7 +31,7 @@ import SwiftUI public struct AppMenuImage: View, TunnelContextProviding { @ObservedObject - var connectionObserver: ConnectionObserver + public var connectionObserver: ConnectionObserver public init(connectionObserver: ConnectionObserver) { self.connectionObserver = connectionObserver diff --git a/Passepartout/Library/Sources/AppUI/Views/AppMenu/AppWindow.swift b/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppWindow.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/AppMenu/AppWindow.swift rename to Passepartout/Library/Sources/AppUIMain/Views/AppMenu/AppWindow.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/DebugLogView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/DebugLogView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/DebugLogView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/DebugLogView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/DiagnosticsView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/DiagnosticsView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/DiagnosticsView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/DiagnosticsView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/ReportIssueButton.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/ReportIssueButton.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/ReportIssueButton.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/ReportIssueButton.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/iOS/DebugLogView+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/iOS/DebugLogView+iOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/iOS/DebugLogView+iOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/iOS/DebugLogView+iOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/macOS/DebugLogView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/macOS/DebugLogView+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/macOS/DebugLogView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/macOS/DebugLogView+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/DNSView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/DNSView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/DNSView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/DNSView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/DNSModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/DNSModule+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/DNSModule+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/DNSModule+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/IPModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/IPModule+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/IPModule+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/IPModule+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/OnDemandModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OnDemandModule+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/OnDemandModule+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OnDemandModule+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/OpenVPNModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/OpenVPNModule+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/WireGuardModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/WireGuardModule+Extensions.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/Extensions/WireGuardModule+Extensions.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/WireGuardModule+Extensions.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/HTTPProxyView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/HTTPProxyView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/HTTPProxyView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/HTTPProxyView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/IPView+Route.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/IPView+Route.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/IPView+Route.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/IPView+Route.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/IPView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/IPView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/IPView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/IPView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/OnDemandView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/OnDemandView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/OnDemandView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/OnDemandView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView+Configuration.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/OpenVPNView+Configuration.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView+Configuration.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/OpenVPNView+Configuration.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView+Credentials.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/OpenVPNView+Credentials.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView+Credentials.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/OpenVPNView+Credentials.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/OpenVPNView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/OpenVPNView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/WireGuardView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/WireGuardView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Modules/WireGuardView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Modules/WireGuardView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Paywall/PaywallModifier.swift b/Passepartout/Library/Sources/AppUIMain/Views/Paywall/PaywallModifier.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Paywall/PaywallModifier.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Paywall/PaywallModifier.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Paywall/PaywallView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Paywall/PaywallView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Paywall/PaywallView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Paywall/PaywallView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Profile/ModuleDetailView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/ModuleDetailView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Profile/ModuleDetailView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Profile/ModuleDetailView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Profile/ProfileCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift similarity index 98% rename from Passepartout/Library/Sources/AppUI/Views/Profile/ProfileCoordinator.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift index c4f47657..19972683 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Profile/ProfileCoordinator.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift @@ -148,7 +148,7 @@ private extension ProfileCoordinator { #Preview { ProfileCoordinator( profileManager: .mock, - profileEditor: ProfileEditor(profile: .newProfile()), + profileEditor: ProfileEditor(profile: .newMockProfile()), moduleViewFactory: DefaultModuleViewFactory(), modally: false, path: .constant(NavigationPath()), diff --git a/Passepartout/Library/Sources/AppUI/Views/Profile/iOS/ProfileEditView+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift similarity index 98% rename from Passepartout/Library/Sources/AppUI/Views/Profile/iOS/ProfileEditView+iOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift index c0690c0d..ce363b7f 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Profile/iOS/ProfileEditView+iOS.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift @@ -166,7 +166,7 @@ private extension ProfileEditView { #Preview { NavigationStack { ProfileEditView( - profileEditor: ProfileEditor(profile: .newProfile()), + profileEditor: ProfileEditor(profile: .newMockProfile()), moduleViewFactory: DefaultModuleViewFactory(), path: .constant(NavigationPath()) ) diff --git a/Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ModuleListView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ModuleListView+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ModuleListView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ModuleListView+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ProfileGeneralView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ProfileGeneralView+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ProfileGeneralView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ProfileGeneralView+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ProfileSplitView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift similarity index 98% rename from Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ProfileSplitView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift index 793f75d5..30ec91d3 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Profile/macOS/ProfileSplitView+macOS.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift @@ -122,7 +122,7 @@ private extension ProfileSplitView { #Preview { ProfileSplitView( - profileEditor: ProfileEditor(profile: .newProfile()), + profileEditor: ProfileEditor(profile: .newMockProfile()), moduleViewFactory: DefaultModuleViewFactory() ) .withMockEnvironment() diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/ProviderContentModifier.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/ProviderContentModifier.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/ProviderContentModifier.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/ProviderContentModifier.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/ProviderPicker.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/ProviderPicker.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/ProviderPicker.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/ProviderPicker.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView+Model.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNFiltersView+Model.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView+Model.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNFiltersView+Model.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNFiltersView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNFiltersView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderContentModifier.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderContentModifier.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderContentModifier.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderContentModifier.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerCoordinator.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerCoordinator.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerCoordinator.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerCoordinator.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/VPNProviderServerView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNProviderServerView+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/iOS/VPNProviderServerView+iOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNProviderServerView+iOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/iOS/VPNProviderServerView+iOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNProviderServerView+macOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/macOS/VPNProviderServerView+macOS.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNProviderServerView+macOS.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Provider/macOS/VPNProviderServerView+macOS.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Settings/SettingsSectionGroup.swift b/Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsSectionGroup.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Settings/SettingsSectionGroup.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsSectionGroup.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Settings/SettingsView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Settings/SettingsView.swift rename to Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsView.swift diff --git a/Passepartout/Library/Sources/AppUITV/AppUITV.swift b/Passepartout/Library/Sources/AppUITV/AppUITV.swift new file mode 100644 index 00000000..dde25466 --- /dev/null +++ b/Passepartout/Library/Sources/AppUITV/AppUITV.swift @@ -0,0 +1,33 @@ +// +// AppUITV.swift +// Passepartout +// +// Created by Davide De Rosa on 10/29/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +@_exported import AppUI +import Foundation + +public enum AppUITV: AppUIConfiguring { + public static func configure(with context: AppContext) { + AppUI.configure(with: context) + } +} diff --git a/Passepartout/Library/Sources/AppUITV/Views/AppCoordinator.swift b/Passepartout/Library/Sources/AppUITV/Views/AppCoordinator.swift new file mode 100644 index 00000000..1388f628 --- /dev/null +++ b/Passepartout/Library/Sources/AppUITV/Views/AppCoordinator.swift @@ -0,0 +1,60 @@ +// +// AppCoordinator.swift +// Passepartout +// +// Created by Davide De Rosa on 10/29/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import AppLibrary +import PassepartoutKit +import SwiftUI + +// FIXME: ###, UI for Apple TV + +public struct AppCoordinator: View { + private let profileManager: ProfileManager + + private let tunnel: Tunnel + + private let registry: Registry + + public init(profileManager: ProfileManager, tunnel: Tunnel, registry: Registry) { + self.profileManager = profileManager + self.tunnel = tunnel + self.registry = registry + } + + public var body: some View { + ProfilesView(profileManager: profileManager) + } +} + +struct ProfilesView: View { + + @ObservedObject + var profileManager: ProfileManager + + var body: some View { + ForEach(profileManager.headers, id: \.id) { + Text($0.name) + } + } +} diff --git a/Passepartout/Library/Sources/UtilsLibrary/Views/EditableListSection.swift b/Passepartout/Library/Sources/UtilsLibrary/Views/EditableListSection.swift index f51bfb1c..bce7ff76 100644 --- a/Passepartout/Library/Sources/UtilsLibrary/Views/EditableListSection.swift +++ b/Passepartout/Library/Sources/UtilsLibrary/Views/EditableListSection.swift @@ -23,6 +23,8 @@ // along with Passepartout. If not, see . // +#if !os(tvOS) + import SwiftUI public protocol EditableValue: Hashable, CustomStringConvertible { @@ -257,3 +259,5 @@ private extension EditableListSection { } } } + +#endif diff --git a/Passepartout/Library/Sources/UtilsLibrary/Views/LockableView.swift b/Passepartout/Library/Sources/UtilsLibrary/Views/LockableView.swift index f38bd900..d7c79b2c 100644 --- a/Passepartout/Library/Sources/UtilsLibrary/Views/LockableView.swift +++ b/Passepartout/Library/Sources/UtilsLibrary/Views/LockableView.swift @@ -23,6 +23,8 @@ // along with Passepartout. If not, see . // +#if !os(tvOS) + import SwiftUI public struct LockableView: View { @@ -142,3 +144,5 @@ private extension LockableView { } } } + +#endif diff --git a/Passepartout/Library/Sources/UtilsLibrary/Views/LongContentView.swift b/Passepartout/Library/Sources/UtilsLibrary/Views/LongContentView.swift index 00d24696..a4290479 100644 --- a/Passepartout/Library/Sources/UtilsLibrary/Views/LongContentView.swift +++ b/Passepartout/Library/Sources/UtilsLibrary/Views/LongContentView.swift @@ -23,6 +23,8 @@ // along with Passepartout. If not, see . // +#if !os(tvOS) + import SwiftUI public struct LongContentView: View { @@ -89,3 +91,5 @@ public struct LongContentLink: View { } } } + +#endif diff --git a/Passepartout/Library/Tests/AppUITests/IssueTests.swift b/Passepartout/Library/Tests/AppUIMainTests/IssueTests.swift similarity index 98% rename from Passepartout/Library/Tests/AppUITests/IssueTests.swift rename to Passepartout/Library/Tests/AppUIMainTests/IssueTests.swift index 1976771a..43f93b7b 100644 --- a/Passepartout/Library/Tests/AppUITests/IssueTests.swift +++ b/Passepartout/Library/Tests/AppUIMainTests/IssueTests.swift @@ -23,7 +23,7 @@ // along with Passepartout. If not, see . // -@testable import AppUI +@testable import AppUIMain import Foundation import XCTest diff --git a/Passepartout/Library/Tests/AppUITests/ProfileImporterTests.swift b/Passepartout/Library/Tests/AppUIMainTests/ProfileImporterTests.swift similarity index 99% rename from Passepartout/Library/Tests/AppUITests/ProfileImporterTests.swift rename to Passepartout/Library/Tests/AppUIMainTests/ProfileImporterTests.swift index 80d8c064..bdee4be3 100644 --- a/Passepartout/Library/Tests/AppUITests/ProfileImporterTests.swift +++ b/Passepartout/Library/Tests/AppUIMainTests/ProfileImporterTests.swift @@ -24,7 +24,7 @@ // @testable import AppLibrary -@testable import AppUI +@testable import AppUIMain import Combine import Foundation import PassepartoutKit diff --git a/Passepartout/Passepartout.xctestplan b/Passepartout/Passepartout.xctestplan index 2dcdc769..8086757d 100644 --- a/Passepartout/Passepartout.xctestplan +++ b/Passepartout/Passepartout.xctestplan @@ -25,6 +25,13 @@ "identifier" : "AppUITests", "name" : "AppUITests" } + }, + { + "target" : { + "containerPath" : "container:Passepartout\/Library", + "identifier" : "AppUIMainTests", + "name" : "AppUIMainTests" + } } ], "version" : 1