Rewrite app in SwiftUI

This commit is contained in:
Davide De Rosa 2022-04-12 15:09:14 +02:00
parent 2c9be6334a
commit 4aba5f46aa
2103 changed files with 27410 additions and 40371 deletions

View File

@ -1,6 +1,6 @@
APP_ROOT="Passepartout/App/iOS" APP_ROOT="Passepartout/App/iOS"
MATCH_PLATFORM="ios" MATCH_PLATFORM="ios"
GYM_SCHEME="Passepartout-iOS" GYM_SCHEME="Passepartout"
SCAN_DEVICE="iPhone 12" SCAN_DEVICE="iPhone 12"
DELIVER_PLATFORM="ios" DELIVER_PLATFORM="ios"
PILOT_PLATFORM="ios" PILOT_PLATFORM="ios"

View File

@ -1,7 +1,9 @@
APP_ROOT="Passepartout/App/macOS" APP_ROOT="Passepartout/App/macOS"
MATCH_PLATFORM="macos" MATCH_PLATFORM="catalyst"
MATCH_ADDITIONAL_CERT_TYPES="mac_installer_distribution" MATCH_ADDITIONAL_CERT_TYPES="mac_installer_distribution"
GYM_SCHEME="Passepartout-macOS" GYM_SCHEME="Passepartout"
SCAN_DESTINATION="platform=macOS"
# not sure about these
SCAN_DESTINATION="platform=iOS"
DELIVER_PLATFORM="osx" DELIVER_PLATFORM="osx"
PILOT_PLATFORM="osx" PILOT_PLATFORM="osx"

View File

@ -146,7 +146,7 @@ jobs:
publish_to_app_store: publish_to_app_store:
name: Publish to App Store name: Publish to App Store
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build_upload, submit_for_app_review] needs: submit_for_app_review
environment: environment:
name: app_store name: app_store
env: env:

1
.gitignore vendored
View File

@ -14,7 +14,6 @@ iap/
templates/ templates/
.env.secret* .env.secret*
Preview.html Preview.html
l10n
passepartout-translations.zip passepartout-translations.zip
default.profraw default.profraw
asc-key.json asc-key.json

4
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "Submodules/fastlane-ci-templates"] [submodule "Submodules/fastlane-ci-templates"]
path = Submodules/fastlane-ci-templates path = Submodules/fastlane-ci-templates
url = https://github.com/keeshux/fastlane-ci-templates url = https://github.com/keeshux/fastlane-ci-templates
[submodule "PassepartoutCore/Sources/PassepartoutCore/API"] [submodule "PassepartoutCore/Sources/PassepartoutServices/API"]
path = PassepartoutCore/Sources/PassepartoutCore/API path = PassepartoutCore/Sources/PassepartoutServices/API
url = https://github.com/passepartoutvpn/api url = https://github.com/passepartoutvpn/api

View File

@ -33,4 +33,6 @@ CFG_GROUP_ID = com.algoritmico.Passepartout
CFG_APPSTORE_ID = 1433648537 CFG_APPSTORE_ID = 1433648537
CFG_COPYRIGHT = Copyright © 2022 Davide De Rosa. All rights reserved. CFG_COPYRIGHT = Copyright © 2022 Davide De Rosa. All rights reserved.
PATH = $(PATH):/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin
#include? "Secret.xcconfig" #include? "Secret.xcconfig"

View File

@ -3,7 +3,7 @@ GEM
specs: specs:
CFPropertyList (3.0.5) CFPropertyList (3.0.5)
rexml rexml
activesupport (6.1.4.4) activesupport (6.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -17,27 +17,27 @@ GEM
artifactory (3.0.15) artifactory (3.0.15)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.552.0) aws-partitions (1.570.0)
aws-sdk-core (3.126.0) aws-sdk-core (3.130.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0) aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
jmespath (~> 1.0) jmespath (~> 1.0)
aws-sdk-kms (1.54.0) aws-sdk-kms (1.55.0)
aws-sdk-core (~> 3, >= 3.126.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.112.0) aws-sdk-s3 (1.113.0)
aws-sdk-core (~> 3, >= 3.126.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0) aws-sigv4 (1.4.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4) babosa (1.0.4)
claide (1.1.0) claide (1.1.0)
cocoapods (1.11.2) cocoapods (1.11.3)
addressable (~> 2.8) addressable (~> 2.8)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.11.2) cocoapods-core (= 1.11.3)
cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0)
@ -52,7 +52,7 @@ GEM
nap (~> 1.0) nap (~> 1.0)
ruby-macho (>= 1.0, < 3.0) ruby-macho (>= 1.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0) xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.11.2) cocoapods-core (1.11.3)
activesupport (>= 5.0, < 7) activesupport (>= 5.0, < 7)
addressable (~> 2.8) addressable (~> 2.8)
algoliasearch (~> 1.0) algoliasearch (~> 1.0)
@ -63,7 +63,7 @@ GEM
public_suffix (~> 4.0) public_suffix (~> 4.0)
typhoeus (~> 1.0) typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5) cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.5.1) cocoapods-downloader (1.6.1)
cocoapods-plugins (1.0.0) cocoapods-plugins (1.0.0)
nap nap
cocoapods-search (1.0.1) cocoapods-search (1.0.1)
@ -75,7 +75,7 @@ GEM
colored2 (3.1.2) colored2 (3.1.2)
commander (4.6.0) commander (4.6.0)
highline (~> 2.0.0) highline (~> 2.0.0)
concurrent-ruby (1.1.9) concurrent-ruby (1.1.10)
declarative (0.0.20) declarative (0.0.20)
digest-crc (0.6.4) digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0) rake (>= 12.0.0, < 14.0.0)
@ -86,8 +86,8 @@ GEM
escape (0.0.4) escape (0.0.4)
ethon (0.15.0) ethon (0.15.0)
ffi (>= 1.15.0) ffi (>= 1.15.0)
excon (0.91.0) excon (0.92.1)
faraday (1.9.3) faraday (1.10.0)
faraday-em_http (~> 1.0) faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0) faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1) faraday-excon (~> 1.1)
@ -116,7 +116,7 @@ GEM
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fastimage (2.2.6) fastimage (2.2.6)
fastlane (2.204.2) fastlane (2.205.1)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0) addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0) artifactory (~> 3.0)
@ -179,10 +179,10 @@ GEM
google-cloud-core (1.6.0) google-cloud-core (1.6.0)
google-cloud-env (~> 1.0) google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0) google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0) google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 2.0) faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0) google-cloud-errors (1.2.0)
google-cloud-storage (1.36.0) google-cloud-storage (1.36.1)
addressable (~> 2.8) addressable (~> 2.8)
digest-crc (~> 0.4) digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1) google-apis-iamcredentials_v1 (~> 0.1)
@ -190,8 +190,8 @@ GEM
google-cloud-core (~> 1.6) google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0) mini_mime (~> 1.0)
googleauth (1.1.0) googleauth (1.1.2)
faraday (>= 0.17.3, < 2.0) faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0) jwt (>= 1.4, < 3.0)
memoist (~> 0.16) memoist (~> 0.16)
multi_json (~> 1.11) multi_json (~> 1.11)
@ -201,9 +201,9 @@ GEM
http-cookie (1.0.4) http-cookie (1.0.4)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
i18n (1.9.1) i18n (1.10.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jmespath (1.5.0) jmespath (1.6.1)
json (2.6.1) json (2.6.1)
jwt (2.3.0) jwt (2.3.0)
memoist (0.16.2) memoist (0.16.2)
@ -233,9 +233,9 @@ GEM
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
security (0.1.3) security (0.1.3)
signet (0.16.0) signet (0.16.1)
addressable (~> 2.8) addressable (~> 2.8)
faraday (>= 0.17.3, < 2.0) faraday (>= 0.17.5, < 3.0)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simctl (1.6.8) simctl (1.6.8)
@ -256,7 +256,7 @@ GEM
uber (0.1.0) uber (0.1.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.8) unf_ext (0.0.8.1)
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
webrick (1.7.0) webrick (1.7.0)
word_wrap (1.0.0) word_wrap (1.0.0)

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,6 @@
{ {
"object": { "object": {
"pins": [ "pins": [
{
"package": "Convenience",
"repositoryURL": "https://github.com/keeshux/convenience",
"state": {
"branch": null,
"revision": "347105ec0ce27cd4255acf9875fd60ad1f213801",
"version": null
}
},
{ {
"package": "DTFoundation", "package": "DTFoundation",
"repositoryURL": "https://github.com/Cocoanetics/DTFoundation.git", "repositoryURL": "https://github.com/Cocoanetics/DTFoundation.git",
@ -20,12 +11,12 @@
} }
}, },
{ {
"package": "FontAwesome", "package": "GenericJSON",
"repositoryURL": "https://github.com/thii/FontAwesome.swift", "repositoryURL": "https://github.com/zoul/generic-json-swift",
"state": { "state": {
"branch": null, "branch": null,
"revision": "07883a32d49dfc7bdedbeea115067b53dfbeeb43", "revision": "a137894b2a217abe489cdf1ea287e6f7819b1051",
"version": "1.9.1" "version": "2.0.1"
} }
}, },
{ {
@ -37,15 +28,6 @@
"version": "1.0.6" "version": "1.0.6"
} }
}, },
{
"package": "MBProgressHUD",
"repositoryURL": "https://github.com/jdg/MBProgressHUD",
"state": {
"branch": null,
"revision": "bca42b801100b2b3a4eda0ba8dd33d858c780b0d",
"version": "1.2.0"
}
},
{ {
"package": "openssl-apple", "package": "openssl-apple",
"repositoryURL": "https://github.com/passepartoutvpn/openssl-apple", "repositoryURL": "https://github.com/passepartoutvpn/openssl-apple",
@ -64,22 +46,13 @@
"version": "1.9.5" "version": "1.9.5"
} }
}, },
{
"package": "TunnelKit",
"repositoryURL": "https://github.com/passepartoutvpn/tunnelkit",
"state": {
"branch": null,
"revision": "88544e4877f00990ef699873f3505fff606ab64b",
"version": "4.1.0"
}
},
{ {
"package": "WireGuardKit", "package": "WireGuardKit",
"repositoryURL": "https://git.zx2c4.com/wireguard-apple", "repositoryURL": "https://github.com/passepartoutvpn/wireguard-apple",
"state": { "state": {
"branch": null, "branch": null,
"revision": "10da5cfdef362889b438cfbeff867a74e6d717fd", "revision": "d3b8f1ac6f3361d69bd3daf8aee3c43012c6ec0b",
"version": "1.0.15-26" "version": "1.0.16"
} }
} }
] ]

View File

@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E5202F6259F573500CBAB56"
BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-macOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E5203B4259F5F3F00CBAB56"
BuildableName = "PassepartoutTunnel.appex"
BlueprintName = "PassepartoutTunnel-macOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PassepartoutCoreTests"
BuildableName = "PassepartoutCoreTests"
BlueprintName = "PassepartoutCoreTests"
ReferencedContainer = "container:PassepartoutCore">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E5202F6259F573500CBAB56"
BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-macOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E5202F6259F573500CBAB56"
BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-macOS"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -16,7 +16,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "0E57F63720C83FC5008323CF" BlueprintIdentifier = "0E57F63720C83FC5008323CF"
BuildableName = "Passepartout.app" BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-iOS" BlueprintName = "Passepartout"
ReferencedContainer = "container:Passepartout.xcodeproj"> ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
@ -29,8 +29,36 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "0EDE8DBE20C86910004C739C" BlueprintIdentifier = "0EDE8DBE20C86910004C739C"
BuildableName = "PassepartoutTunnel.appex" BuildableName = "PassepartoutOpenVPNTunnel.appex"
BlueprintName = "PassepartoutTunnel-iOS" BlueprintName = "OpenVPNTunnel"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0ECF71F327B6D9CD00CDB528"
BuildableName = "WireGuardGo"
BlueprintName = "WireGuardGo"
ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0ED2B33E27D3C77800FD8EA9"
BuildableName = "PassepartoutWireGuardTunnel.appex"
BlueprintName = "WireGuardTunnel"
ReferencedContainer = "container:Passepartout.xcodeproj"> ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
@ -46,21 +74,11 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "0E57F63720C83FC5008323CF" BlueprintIdentifier = "0E57F63720C83FC5008323CF"
BuildableName = "Passepartout.app" BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-iOS" BlueprintName = "Passepartout"
ReferencedContainer = "container:Passepartout.xcodeproj"> ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<Testables> <Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PassepartoutCoreTests"
BuildableName = "PassepartoutCoreTests"
BlueprintName = "PassepartoutCoreTests"
ReferencedContainer = "container:PassepartoutCore">
</BuildableReference>
</TestableReference>
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
@ -79,14 +97,41 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "0E57F63720C83FC5008323CF" BlueprintIdentifier = "0E57F63720C83FC5008323CF"
BuildableName = "Passepartout.app" BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-iOS" BlueprintName = "Passepartout"
ReferencedContainer = "container:Passepartout.xcodeproj"> ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-com.apple.CoreData.SQLDebug 0"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-com.apple.CoreData.Logging.stderr 0 "
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-com.apple.CoreData.CloudKitDebug 0"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-com.apple.CoreData.ConcurrencyDebug 0"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-com.apple.CoreData.MigrationDebug 0"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<EnvironmentVariables> <EnvironmentVariables>
<EnvironmentVariable <EnvironmentVariable
key = "FULL_VERSION" key = "APP_TYPE"
value = "" value = "2"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "LOG_LEVEL"
value = "0"
isEnabled = "YES"> isEnabled = "YES">
</EnvironmentVariable> </EnvironmentVariable>
</EnvironmentVariables> </EnvironmentVariables>
@ -103,7 +148,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "0E57F63720C83FC5008323CF" BlueprintIdentifier = "0E57F63720C83FC5008323CF"
BuildableName = "Passepartout.app" BuildableName = "Passepartout.app"
BlueprintName = "Passepartout-iOS" BlueprintName = "Passepartout"
ReferencedContainer = "container:Passepartout.xcodeproj"> ReferencedContainer = "container:Passepartout.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>

View File

@ -1,182 +0,0 @@
//
// Descriptible.swift
// Passepartout
//
// Created by Davide De Rosa on 1/12/21.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import PassepartoutCore
public protocol UIDescriptible {
var uiDescription: String { get }
}
extension OpenVPN.Cipher: UIDescriptible {
public var uiDescription: String {
return description
}
}
extension OpenVPN.Digest: UIDescriptible {
public var uiDescription: String {
return description
}
}
extension OpenVPN.CompressionFraming: UIDescriptible {
public var uiDescription: String {
let V = L10n.Configuration.Cells.self
switch self {
case .disabled:
return L10n.Global.Values.disabled
case .compLZO:
return V.CompressionFraming.Value.lzo
case .compress, .compressV2:
return V.CompressionFraming.Value.compress
}
}
}
extension OpenVPN.CompressionAlgorithm: UIDescriptible {
public var uiDescription: String {
let V = L10n.Configuration.Cells.self
switch self {
case .disabled:
return L10n.Global.Values.disabled
case .LZO:
return V.CompressionAlgorithm.Value.lzo
case .other:
return V.CompressionAlgorithm.Value.other
}
}
}
extension OpenVPN.ConfigurationBuilder {
public var uiDescriptionForTLSWrap: String {
let V = L10n.Configuration.Cells.self
if let strategy = tlsWrap?.strategy {
switch strategy {
case .auth:
return V.TlsWrapping.Value.auth
case .crypt:
return V.TlsWrapping.Value.crypt
}
} else {
return L10n.Global.Values.disabled
}
}
public var uiDescriptionForKeepAlive: String {
let V = L10n.Configuration.Cells.self
if let keepAlive = keepAliveInterval, keepAlive > 0 {
return V.KeepAlive.Value.seconds(Int(keepAlive))
} else {
return L10n.Global.Values.disabled
}
}
public var uiDescriptionForClientCertificate: String {
let V = L10n.Configuration.Cells.Client.Value.self
return (clientCertificate != nil) ? V.enabled : V.disabled
}
public var uiDescriptionForEKU: String {
let V = L10n.Global.Values.self
return (checksEKU ?? false) ? V.enabled : V.disabled
}
public var uiDescriptionForRenegotiatesAfter: String {
let V = L10n.Configuration.Cells.self
if let reneg = renegotiatesAfter, reneg > 0 {
return V.RenegotiationSeconds.Value.after(TimeInterval(reneg).localized)
} else {
return L10n.Global.Values.disabled
}
}
public var uiDescriptionForRandomizeEndpoint: String {
let V = L10n.Global.Values.self
return (randomizeEndpoint ?? false) ? V.enabled : V.disabled
}
public var uiDescriptionForXOR: String {
let V = L10n.Global.Values.self
guard let mask = xorMask, mask != 0 else {
return V.disabled
}
return String(format: "0x%02x", UInt8(mask))
}
}
extension NetworkChoice: CustomStringConvertible {
public var description: String {
switch self {
case .client:
return L10n.NetworkChoice.client
case .server:
return L10n.NetworkChoice.server
case .manual:
return L10n.Global.Values.manual
}
}
}
extension DNSProtocol: CustomStringConvertible {
public var description: String {
switch self {
case .plain:
return "Cleartext"
case .https:
return "HTTPS"
case .tls:
return "TLS"
}
}
}
extension VPNStatus: UIDescriptible {
public var uiDescription: String {
switch self {
case .connecting:
return L10n.Vpn.connecting
case .connected:
return L10n.Vpn.active
case .disconnecting:
return L10n.Vpn.disconnecting
case .disconnected:
return L10n.Vpn.inactive
}
}
}

View File

@ -0,0 +1,40 @@
#!/bin/sh
# build_wireguard_go_bridge.sh - Builds WireGuardKitGo
#
# Figures out the directory where the wireguard-apple SPM package
# is checked out by Xcode (so that it works when building as well as
# archiving), then cd-s to the WireGuardKitGo directory
# and runs make there.
project_data_dir="$BUILD_DIR"
# The wireguard-apple README suggests using ${BUILD_DIR%Build/*}, which
# doesn't seem to work. So here, we do the equivalent in script.
while true; do
parent_dir=$(dirname "$project_data_dir")
basename=$(basename "$project_data_dir")
project_data_dir="$parent_dir"
if [ "$basename" = "Build" ]; then
break
fi
done
# The wireguard-apple README looks into
# SourcePackages/checkouts/wireguard-apple, but Xcode seems to place the
# sources in SourcePackages/checkouts/ so just playing it safe and
# trying both.
checkouts_dir="$project_data_dir"/SourcePackages/checkouts
if [ -e "$checkouts_dir"/wireguard-apple ]; then
checkouts_dir="$checkouts_dir"/wireguard-apple
fi
wireguard_go_dir="$checkouts_dir"/Sources/WireGuardKitGo
# To ensure we have Go in our path, we add where
# Homebrew generally installs executables
export PATH=${PATH}:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin
cd "$wireguard_go_dir" && /usr/bin/make

View File

@ -0,0 +1,9 @@
# Type a script or drag a script file from your workspace to insert its path.
CD_PROVIDERS_DIR="$PROJECT_TEMP_DIR/../PassepartoutCore.build/Debug-iphonesimulator/PassepartoutProviders.build/DerivedSources/CoreDataGenerated/Providers"
CD_CORE_DIR="$PROJECT_TEMP_DIR/../PassepartoutCore.build/Debug-iphonesimulator/PassepartoutCore.build/DerivedSources/CoreDataGenerated/Core"
if [ -d "$CD_PROVIDERS_DIR" ]; then
cp "$CD_PROVIDERS_DIR"/* "$PROJECT_DIR/PassepartoutCore/Sources/PassepartoutProviders/DataModels"
fi
if [ -d "$CD_CORE_DIR" ]; then
cp "$CD_CORE_DIR"/* "$PROJECT_DIR/PassepartoutCore/Sources/PassepartoutCore/DataModels"
fi

View File

@ -2,20 +2,36 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.algoritmico.Passepartout</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
<key>com.apple.developer.networking.networkextension</key> <key>com.apple.developer.networking.networkextension</key>
<array> <array>
<string>packet-tunnel-provider</string> <string>packet-tunnel-provider</string>
</array> </array>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
<key>com.apple.developer.siri</key>
<true/>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>$(TeamIdentifierPrefix)group.$(CFG_GROUP_ID)</string> <string>group.$(CFG_GROUP_ID)</string>
</array> </array>
<key>com.apple.security.files.user-selected.read-only</key> <key>com.apple.security.files.user-selected.read-only</key>
<true/> <true/>
<key>com.apple.security.network.client</key> <key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.personal-information.location</key>
<true/>
<key>keychain-access-groups</key> <key>keychain-access-groups</key>
<array> <array>
<string>$(AppIdentifierPrefix)group.com.algoritmico.Passepartout</string> <string>$(AppIdentifierPrefix)group.com.algoritmico.Passepartout</string>

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x68",
"green" : "0x9C",
"red" : "0xD6"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.443",
"green" : "0.365",
"red" : "0.318"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,208 @@
//
// AppContext.swift
// Passepartout
//
// Created by Davide De Rosa on 3/17/22.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import CoreData
import Combine
import PassepartoutCore
import PassepartoutServices
@MainActor
class AppContext {
static let shared = AppContext()
private let persistenceManager: PersistenceManager
let appManager: AppManager
let providerManager: ProviderManager
let profileManager: ProfileManager
let vpnManager: VPNManager
let productManager: ProductManager
let intentsManager: IntentsManager
let reviewer: Reviewer
private var cancellables: Set<AnyCancellable> = []
private init() {
// core
appManager = AppManager()
appManager.logLevel = Constants.Log.logLevel
appManager.logFile = Constants.Log.appFileURL
appManager.logFormat = Constants.Log.appLogFormat
appManager.tunnelLogFormat = Constants.Log.tunnelLogFormat
appManager.configureLogging()
pp_log.info("Logging to: \(appManager.logFile!)")
persistenceManager = PersistenceManager(author: appManager.persistenceAuthor)
providerManager = ProviderManager(
appBuild: Constants.Global.appBuildNumber,
bundleServices: DefaultWebServices.bundledServices(
withVersion: Constants.Services.version
),
webServices: DefaultWebServices(
Constants.Services.version,
Constants.Repos.api,
timeout: Constants.Services.connectivityTimeout
),
persistence: persistenceManager.providersPersistence(
withName: Constants.Persistence.providersContainerName
)
)
profileManager = ProfileManager(
providerManager: providerManager,
appGroup: Constants.App.appGroupId,
keychainLabel: Unlocalized.Keychain.passwordLabel,
strategy: ProfileManager.CoreDataStrategy(
persistence: persistenceManager.profilesPersistence(
withName: Constants.Persistence.profilesContainerName
)
)
)
#if targetEnvironment(simulator)
vpnManager = VPNManager(
appManager: appManager,
profileManager: profileManager,
providerManager: providerManager,
strategy: VPNManager.MockStrategy()
)
#else
vpnManager = VPNManager(
appManager: appManager,
profileManager: profileManager,
providerManager: providerManager,
strategy: VPNManager.TunnelKitStrategy(
appGroup: Constants.App.appGroupId,
tunnelBundleIdentifier: Constants.App.tunnelBundleId
)
)
#endif
// app
productManager = ProductManager(.init(
appType: Constants.InApp.appType,
lastFullVersionBuild: Constants.InApp.lastFullVersionBuild
))
intentsManager = IntentsManager()
reviewer = Reviewer()
// post
configureObjects()
}
private func configureObjects() {
// core
profileManager.availabilityFilter = {
self.isEligibleProfile(withHeader: $0)
}
profileManager.activeProfileId = appManager.activeProfileId
providerManager.rateLimitMilliseconds = Constants.RateLimit.providerManager
vpnManager.rateLimitMilliseconds = Constants.RateLimit.vpnManager
vpnManager.isOnDemandSupported = {
self.isEligibleForOnDemand()
}
// app
reviewer.eventCountBeforeRating = Constants.Rating.eventCount
vpnManager.currentState.$vpnStatus
.removeDuplicates()
.sink {
if $0 == .connected {
pp_log.info("VPN successful connection, report to Reviewer")
self.reviewer.reportEvent()
}
}.store(in: &cancellables)
}
// eligibility: hide providers not found or not purchased
private func isEligibleProfile(withHeader header: Profile.Header) -> Bool {
guard let providerName = header.providerName else {
return true // always eligible for non-provider profiles
}
guard productManager.isEligible(forProvider: providerName) else {
// pp_log.debug("Not eligible for provider \(metadata.name)")
return false
}
return true
}
// eligibility: reset on-demand rules if no trusted networks
private func isEligibleForOnDemand() -> Bool {
guard productManager.isEligible(forFeature: .trustedNetworks) else {
pp_log.warning("Ignore on-demand rules, not eligible for trusted networks")
return false
}
return true
}
}
extension AppManager {
static let shared = AppContext.shared.appManager
}
extension ProfileManager {
static let shared = AppContext.shared.profileManager
}
extension ProviderManager {
static let shared = AppContext.shared.providerManager
}
extension VPNManager {
static let shared = AppContext.shared.vpnManager
}
extension ProductManager {
static let shared = AppContext.shared.productManager
}
extension IntentsManager {
static let shared = AppContext.shared.intentsManager
}
extension Reviewer {
static let shared = AppContext.shared.reviewer
}
extension VPNManager.ObservableState {
@MainActor
static let shared = AppContext.shared.vpnManager.currentState
}

View File

@ -0,0 +1,282 @@
//
// Constants+Extensions.swift
// Passepartout
//
// Created by Davide De Rosa on 9/15/18.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import UniformTypeIdentifiers
import PassepartoutCore
import SwiftyBeaver
extension Constants {
private static let bundleConfig = Bundle.main.infoDictionary?["com.algoritmico.Passepartout.config"] as? [String: Any]
}
extension Constants {
enum App {
static let appLauncherId = bundleConfig?["app_launcher_id"] as? String ?? "DUMMY_app_launcher_id"
static let appStoreId = bundleConfig?["appstore_id"] as? String ?? "DUMMY_appstore_id"
static let appGroupId = bundleConfig?["group_id"] as? String ?? "DUMMY_group_id"
static func tunnelBundleId(_ vpnProtocol: VPNProtocolType) -> String {
guard let identifier = Bundle.main.infoDictionary?[kCFBundleIdentifierKey as String] as? String else {
fatalError("Missing kCFBundleIdentifierKey from Info.plist")
}
let prefix = "\(identifier).Tunnel"
switch vpnProtocol {
case .openVPN:
return "\(prefix).OpenVPN"
case .wireGuard:
return "\(prefix).WireGuard"
}
}
}
enum InApp {
static var appType: ProductManager.AppType {
if let envString = ProcessInfo.processInfo.environment["APP_TYPE"],
let envValue = Int(envString),
let testAppType = ProductManager.AppType(rawValue: envValue) {
return testAppType
}
if let infoValue = bundleConfig?["app_type"] as? Int,
let testAppType = ProductManager.AppType(rawValue: infoValue) {
return testAppType
}
return isBeta ? .beta : .freemium
}
#if os(iOS)
static let lastFullVersionBuild: (Int, LocalProduct) = (2016, .fullVersion_iOS)
#else
static let lastFullVersionBuild: (Int, LocalProduct) = (0, .fullVersion_macOS)
#endif
private static var isBeta: Bool {
#if os(iOS)
#if targetEnvironment(simulator)
return true
#else
return Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
#endif
#else
// TODO: production, skip TestFlight on macOS until beta condition is clearly determined
return false
#endif
}
}
}
extension Constants {
enum Activities {
static let enableVPN = "EnableVPNIntent"
static let disableVPN = "DisableVPNIntent"
static let connectVPN = "ConnectVPNIntent"
static let moveToLocation = "MoveToLocationIntent"
static let trustCellularNetwork = "TrustCellularNetworkIntent"
static let trustCurrentNetwork = "TrustCurrentNetworkIntent"
static let untrustCellularNetwork = "UntrustCellularNetworkIntent"
static let untrustCurrentNetwork = "UntrustCurrentNetworkIntent"
}
}
extension Constants {
enum Domain {
static let name = "passepartoutvpn.app"
}
enum Services {
static let version = "v5"
private static let connectivityStrings: [String] = [
"https://www.amazon.com",
"https://www.google.com",
"https://www.twitter.com",
"https://www.facebook.com",
"https://www.instagram.com"
]
static let connectivityURL = URL(string: connectivityStrings.randomElement()!)!
static let connectivityTimeout: TimeInterval = 10.0
}
enum Persistence {
static let profilesContainerName = "Profiles"
static let providersContainerName = "Providers"
}
// milliseconds
enum RateLimit {
static let providerManager = 10000
static let vpnManager = 500
}
enum Log {
static let logLevel: SwiftyBeaver.Level = {
guard let levelString = ProcessInfo.processInfo.environment["LOG_LEVEL"], let levelNum = Int(levelString) else {
return .info
}
return .init(rawValue: levelNum) ?? .info
}()
static let appLogFormat = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M"
private static let appFileName = "Debug.log"
static var appFileURL: URL {
return Files.cachesURL.appendingPathComponent(appFileName)
}
static let tunnelLogFormat = "$DHH:mm:ss$d - $M"
static let tunnelLogMaxBytes = 15000
static let tunnelLogRefreshInterval: TimeInterval = 5.0
}
enum URLs {
static let readme = Repos.apple.appendingPathComponent("blob/master/README.md")
enum iOS {
static let changelog = Repos.apple.appendingPathComponent("blob/master/Passepartout/App/iOS/CHANGELOG.md")
}
enum macOS {
static let changelog = Repos.apple.appendingPathComponent("blob/master/Passepartout/App/macOS/CHANGELOG.md")
}
static let filetypes: [UTType] = [.item]
static let website = URL(string: "https://\(Domain.name)")!
static let faq = website.appendingPathComponent("faq")
static let disclaimer = website.appendingPathComponent("disclaimer")
static let privacyPolicy = website.appendingPathComponent("privacy")
static let donate = website.appendingPathComponent("donate")
static let subreddit = URL(string: "https://www.reddit.com/r/passepartout")!
static let twitch = URL(string: "twitch://stream/keeshux")!
static let twitchFallback = URL(string: "https://twitch.tv/keeshux")!
static let githubSponsors = URL(string: "https://www.github.com/sponsors/passepartoutvpn")!
static let alternativeTo = URL(string: "https://alternativeto.net/software/passepartout-vpn/about/")!
static let openVPNGuidances: [ProviderName: String] = [
.protonvpn: "https://account.protonvpn.com/settings",
.surfshark: "https://my.surfshark.com/vpn/manual-setup/main",
.torguard: "https://torguard.net/clientarea.php?action=changepw",
.windscribe: "https://windscribe.com/getconfig/openvpn"
]
static let referrals: [ProviderName: String] = [
.hideme: "https://member.hide.me/en/checkout?plan=new_default_prices&coupon=6CB-BDB-802&duration=24",
.mullvad: "https://mullvad.net/en/account/create/",
.nordvpn: "https://go.nordvpn.net/SH21Z",
.pia: "https://www.privateinternetaccess.com/pages/buy-vpn/",
.protonvpn: "https://proton.go2cloud.org/SHZ",
.torguard: "https://torguard.net/",
.tunnelbear: "https://www.tunnelbear.com/",
.vyprvpn: "https://www.vyprvpn.com/",
.windscribe: "https://secure.link/kCsD0prd"
]
}
enum Repos {
private static let githubRoot = URL(string: "https://github.com/passepartoutvpn/")!
private static let githubRawRoot = URL(string: "https://\(Domain.name)/")!
private static func github(repo: String) -> URL {
return githubRoot.appendingPathComponent(repo)
}
private static func githubRaw(repo: String) -> URL {
return githubRawRoot.appendingPathComponent(repo)
}
static let apple = github(repo: "passepartout-apple")
static let api = githubRaw(repo: "api")
}
// milliseconds
enum Delays {
static let scrolling = 100
// @available(*, deprecated, message: "for weird animation when using withAnimation() in View.onAppear")
static let xxxAnimateOnAppear = 200
// @available(*, deprecated, message: "file importer stops showing again after closing with swipe down")
static let xxxPresentFileImporter = 200
// @available(*, deprecated, message: "edited shortcut is outdated in delegate")
static let xxxReloadEditedShortcut = 200
}
enum Rating {
#if os(iOS)
static let eventCount = 3
#else
static let eventCount = 10
#endif
}
}
extension Constants {
enum Files {
private static var containerURL: URL {
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: App.appGroupId) else {
print("Unable to access App Group container")
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
return url
}
static let cachesURL: URL = {
let url = containerURL.appendingPathComponent("Library/Caches", isDirectory: true)
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
return url
}()
}
}

View File

@ -0,0 +1,418 @@
// swiftlint:disable all
// Generated using SwiftGen https://github.com/SwiftGen/SwiftGen
#if os(macOS)
import AppKit
#elseif os(iOS)
import UIKit
#elseif os(tvOS) || os(watchOS)
import UIKit
#endif
// Deprecated typealiases
@available(*, deprecated, renamed: "ColorAsset.Color", message: "This typealias will be removed in SwiftGen 7.0")
internal typealias AssetColorTypeAlias = ColorAsset.Color
@available(*, deprecated, renamed: "ImageAsset.Image", message: "This typealias will be removed in SwiftGen 7.0")
internal typealias AssetImageTypeAlias = ImageAsset.Image
// swiftlint:disable superfluous_disable_command file_length implicit_return
// MARK: - Asset Catalogs
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
internal enum Asset {
internal enum Assets {
internal static let accentColor = ColorAsset(name: "accentColor")
internal static let lightTextColor = ColorAsset(name: "lightTextColor")
internal static let logo = ImageAsset(name: "logo")
internal static let primaryColor = ColorAsset(name: "primaryColor")
}
internal enum Flags {
internal enum Flags {
internal static let ad = ImageAsset(name: "flags/ad")
internal static let ae = ImageAsset(name: "flags/ae")
internal static let af = ImageAsset(name: "flags/af")
internal static let ag = ImageAsset(name: "flags/ag")
internal static let ai = ImageAsset(name: "flags/ai")
internal static let al = ImageAsset(name: "flags/al")
internal static let am = ImageAsset(name: "flags/am")
internal static let ao = ImageAsset(name: "flags/ao")
internal static let aq = ImageAsset(name: "flags/aq")
internal static let ar = ImageAsset(name: "flags/ar")
internal static let `as` = ImageAsset(name: "flags/as")
internal static let at = ImageAsset(name: "flags/at")
internal static let au = ImageAsset(name: "flags/au")
internal static let aw = ImageAsset(name: "flags/aw")
internal static let ax = ImageAsset(name: "flags/ax")
internal static let az = ImageAsset(name: "flags/az")
internal static let ba = ImageAsset(name: "flags/ba")
internal static let bb = ImageAsset(name: "flags/bb")
internal static let bd = ImageAsset(name: "flags/bd")
internal static let be = ImageAsset(name: "flags/be")
internal static let bf = ImageAsset(name: "flags/bf")
internal static let bg = ImageAsset(name: "flags/bg")
internal static let bh = ImageAsset(name: "flags/bh")
internal static let bi = ImageAsset(name: "flags/bi")
internal static let bj = ImageAsset(name: "flags/bj")
internal static let bl = ImageAsset(name: "flags/bl")
internal static let bm = ImageAsset(name: "flags/bm")
internal static let bn = ImageAsset(name: "flags/bn")
internal static let bo = ImageAsset(name: "flags/bo")
internal static let bq = ImageAsset(name: "flags/bq")
internal static let br = ImageAsset(name: "flags/br")
internal static let bs = ImageAsset(name: "flags/bs")
internal static let bt = ImageAsset(name: "flags/bt")
internal static let bv = ImageAsset(name: "flags/bv")
internal static let bw = ImageAsset(name: "flags/bw")
internal static let by = ImageAsset(name: "flags/by")
internal static let bz = ImageAsset(name: "flags/bz")
internal static let ca = ImageAsset(name: "flags/ca")
internal static let cc = ImageAsset(name: "flags/cc")
internal static let cd = ImageAsset(name: "flags/cd")
internal static let cf = ImageAsset(name: "flags/cf")
internal static let cg = ImageAsset(name: "flags/cg")
internal static let ch = ImageAsset(name: "flags/ch")
internal static let ci = ImageAsset(name: "flags/ci")
internal static let ck = ImageAsset(name: "flags/ck")
internal static let cl = ImageAsset(name: "flags/cl")
internal static let cm = ImageAsset(name: "flags/cm")
internal static let cn = ImageAsset(name: "flags/cn")
internal static let co = ImageAsset(name: "flags/co")
internal static let cr = ImageAsset(name: "flags/cr")
internal static let cu = ImageAsset(name: "flags/cu")
internal static let cv = ImageAsset(name: "flags/cv")
internal static let cw = ImageAsset(name: "flags/cw")
internal static let cx = ImageAsset(name: "flags/cx")
internal static let cy = ImageAsset(name: "flags/cy")
internal static let cz = ImageAsset(name: "flags/cz")
internal static let de = ImageAsset(name: "flags/de")
internal static let dj = ImageAsset(name: "flags/dj")
internal static let dk = ImageAsset(name: "flags/dk")
internal static let dm = ImageAsset(name: "flags/dm")
internal static let `do` = ImageAsset(name: "flags/do")
internal static let dz = ImageAsset(name: "flags/dz")
internal static let ec = ImageAsset(name: "flags/ec")
internal static let ee = ImageAsset(name: "flags/ee")
internal static let eg = ImageAsset(name: "flags/eg")
internal static let eh = ImageAsset(name: "flags/eh")
internal static let er = ImageAsset(name: "flags/er")
internal static let esCt = ImageAsset(name: "flags/es-ct")
internal static let es = ImageAsset(name: "flags/es")
internal static let et = ImageAsset(name: "flags/et")
internal static let eu = ImageAsset(name: "flags/eu")
internal static let fi = ImageAsset(name: "flags/fi")
internal static let fj = ImageAsset(name: "flags/fj")
internal static let fk = ImageAsset(name: "flags/fk")
internal static let fm = ImageAsset(name: "flags/fm")
internal static let fo = ImageAsset(name: "flags/fo")
internal static let fr = ImageAsset(name: "flags/fr")
internal static let ga = ImageAsset(name: "flags/ga")
internal static let gbEng = ImageAsset(name: "flags/gb-eng")
internal static let gbNir = ImageAsset(name: "flags/gb-nir")
internal static let gbSct = ImageAsset(name: "flags/gb-sct")
internal static let gbWls = ImageAsset(name: "flags/gb-wls")
internal static let gb = ImageAsset(name: "flags/gb")
internal static let gd = ImageAsset(name: "flags/gd")
internal static let ge = ImageAsset(name: "flags/ge")
internal static let gf = ImageAsset(name: "flags/gf")
internal static let gg = ImageAsset(name: "flags/gg")
internal static let gh = ImageAsset(name: "flags/gh")
internal static let gi = ImageAsset(name: "flags/gi")
internal static let gl = ImageAsset(name: "flags/gl")
internal static let gm = ImageAsset(name: "flags/gm")
internal static let gn = ImageAsset(name: "flags/gn")
internal static let gp = ImageAsset(name: "flags/gp")
internal static let gq = ImageAsset(name: "flags/gq")
internal static let gr = ImageAsset(name: "flags/gr")
internal static let gs = ImageAsset(name: "flags/gs")
internal static let gt = ImageAsset(name: "flags/gt")
internal static let gu = ImageAsset(name: "flags/gu")
internal static let gw = ImageAsset(name: "flags/gw")
internal static let gy = ImageAsset(name: "flags/gy")
internal static let hk = ImageAsset(name: "flags/hk")
internal static let hm = ImageAsset(name: "flags/hm")
internal static let hn = ImageAsset(name: "flags/hn")
internal static let hr = ImageAsset(name: "flags/hr")
internal static let ht = ImageAsset(name: "flags/ht")
internal static let hu = ImageAsset(name: "flags/hu")
internal static let id = ImageAsset(name: "flags/id")
internal static let ie = ImageAsset(name: "flags/ie")
internal static let il = ImageAsset(name: "flags/il")
internal static let im = ImageAsset(name: "flags/im")
internal static let `in` = ImageAsset(name: "flags/in")
internal static let io = ImageAsset(name: "flags/io")
internal static let iq = ImageAsset(name: "flags/iq")
internal static let ir = ImageAsset(name: "flags/ir")
internal static let `is` = ImageAsset(name: "flags/is")
internal static let it = ImageAsset(name: "flags/it")
internal static let je = ImageAsset(name: "flags/je")
internal static let jm = ImageAsset(name: "flags/jm")
internal static let jo = ImageAsset(name: "flags/jo")
internal static let jp = ImageAsset(name: "flags/jp")
internal static let ke = ImageAsset(name: "flags/ke")
internal static let kg = ImageAsset(name: "flags/kg")
internal static let kh = ImageAsset(name: "flags/kh")
internal static let ki = ImageAsset(name: "flags/ki")
internal static let km = ImageAsset(name: "flags/km")
internal static let kn = ImageAsset(name: "flags/kn")
internal static let kp = ImageAsset(name: "flags/kp")
internal static let kr = ImageAsset(name: "flags/kr")
internal static let kw = ImageAsset(name: "flags/kw")
internal static let ky = ImageAsset(name: "flags/ky")
internal static let kz = ImageAsset(name: "flags/kz")
internal static let la = ImageAsset(name: "flags/la")
internal static let lb = ImageAsset(name: "flags/lb")
internal static let lc = ImageAsset(name: "flags/lc")
internal static let li = ImageAsset(name: "flags/li")
internal static let lk = ImageAsset(name: "flags/lk")
internal static let lr = ImageAsset(name: "flags/lr")
internal static let ls = ImageAsset(name: "flags/ls")
internal static let lt = ImageAsset(name: "flags/lt")
internal static let lu = ImageAsset(name: "flags/lu")
internal static let lv = ImageAsset(name: "flags/lv")
internal static let ly = ImageAsset(name: "flags/ly")
internal static let ma = ImageAsset(name: "flags/ma")
internal static let mc = ImageAsset(name: "flags/mc")
internal static let md = ImageAsset(name: "flags/md")
internal static let me = ImageAsset(name: "flags/me")
internal static let mf = ImageAsset(name: "flags/mf")
internal static let mg = ImageAsset(name: "flags/mg")
internal static let mh = ImageAsset(name: "flags/mh")
internal static let mk = ImageAsset(name: "flags/mk")
internal static let ml = ImageAsset(name: "flags/ml")
internal static let mm = ImageAsset(name: "flags/mm")
internal static let mn = ImageAsset(name: "flags/mn")
internal static let mo = ImageAsset(name: "flags/mo")
internal static let mp = ImageAsset(name: "flags/mp")
internal static let mq = ImageAsset(name: "flags/mq")
internal static let mr = ImageAsset(name: "flags/mr")
internal static let ms = ImageAsset(name: "flags/ms")
internal static let mt = ImageAsset(name: "flags/mt")
internal static let mu = ImageAsset(name: "flags/mu")
internal static let mv = ImageAsset(name: "flags/mv")
internal static let mw = ImageAsset(name: "flags/mw")
internal static let mx = ImageAsset(name: "flags/mx")
internal static let my = ImageAsset(name: "flags/my")
internal static let mz = ImageAsset(name: "flags/mz")
internal static let na = ImageAsset(name: "flags/na")
internal static let nc = ImageAsset(name: "flags/nc")
internal static let ne = ImageAsset(name: "flags/ne")
internal static let nf = ImageAsset(name: "flags/nf")
internal static let ng = ImageAsset(name: "flags/ng")
internal static let ni = ImageAsset(name: "flags/ni")
internal static let nl = ImageAsset(name: "flags/nl")
internal static let no = ImageAsset(name: "flags/no")
internal static let np = ImageAsset(name: "flags/np")
internal static let nr = ImageAsset(name: "flags/nr")
internal static let nu = ImageAsset(name: "flags/nu")
internal static let nz = ImageAsset(name: "flags/nz")
internal static let om = ImageAsset(name: "flags/om")
internal static let pa = ImageAsset(name: "flags/pa")
internal static let pe = ImageAsset(name: "flags/pe")
internal static let pf = ImageAsset(name: "flags/pf")
internal static let pg = ImageAsset(name: "flags/pg")
internal static let ph = ImageAsset(name: "flags/ph")
internal static let pk = ImageAsset(name: "flags/pk")
internal static let pl = ImageAsset(name: "flags/pl")
internal static let pm = ImageAsset(name: "flags/pm")
internal static let pn = ImageAsset(name: "flags/pn")
internal static let pr = ImageAsset(name: "flags/pr")
internal static let ps = ImageAsset(name: "flags/ps")
internal static let pt = ImageAsset(name: "flags/pt")
internal static let pw = ImageAsset(name: "flags/pw")
internal static let py = ImageAsset(name: "flags/py")
internal static let qa = ImageAsset(name: "flags/qa")
internal static let re = ImageAsset(name: "flags/re")
internal static let ro = ImageAsset(name: "flags/ro")
internal static let rs = ImageAsset(name: "flags/rs")
internal static let ru = ImageAsset(name: "flags/ru")
internal static let rw = ImageAsset(name: "flags/rw")
internal static let sa = ImageAsset(name: "flags/sa")
internal static let sb = ImageAsset(name: "flags/sb")
internal static let sc = ImageAsset(name: "flags/sc")
internal static let sd = ImageAsset(name: "flags/sd")
internal static let se = ImageAsset(name: "flags/se")
internal static let sg = ImageAsset(name: "flags/sg")
internal static let sh = ImageAsset(name: "flags/sh")
internal static let si = ImageAsset(name: "flags/si")
internal static let sj = ImageAsset(name: "flags/sj")
internal static let sk = ImageAsset(name: "flags/sk")
internal static let sl = ImageAsset(name: "flags/sl")
internal static let sm = ImageAsset(name: "flags/sm")
internal static let sn = ImageAsset(name: "flags/sn")
internal static let so = ImageAsset(name: "flags/so")
internal static let sr = ImageAsset(name: "flags/sr")
internal static let ss = ImageAsset(name: "flags/ss")
internal static let st = ImageAsset(name: "flags/st")
internal static let sv = ImageAsset(name: "flags/sv")
internal static let sx = ImageAsset(name: "flags/sx")
internal static let sy = ImageAsset(name: "flags/sy")
internal static let sz = ImageAsset(name: "flags/sz")
internal static let tc = ImageAsset(name: "flags/tc")
internal static let td = ImageAsset(name: "flags/td")
internal static let tf = ImageAsset(name: "flags/tf")
internal static let tg = ImageAsset(name: "flags/tg")
internal static let th = ImageAsset(name: "flags/th")
internal static let tj = ImageAsset(name: "flags/tj")
internal static let tk = ImageAsset(name: "flags/tk")
internal static let tl = ImageAsset(name: "flags/tl")
internal static let tm = ImageAsset(name: "flags/tm")
internal static let tn = ImageAsset(name: "flags/tn")
internal static let to = ImageAsset(name: "flags/to")
internal static let tr = ImageAsset(name: "flags/tr")
internal static let tt = ImageAsset(name: "flags/tt")
internal static let tv = ImageAsset(name: "flags/tv")
internal static let tw = ImageAsset(name: "flags/tw")
internal static let tz = ImageAsset(name: "flags/tz")
internal static let ua = ImageAsset(name: "flags/ua")
internal static let ug = ImageAsset(name: "flags/ug")
internal static let um = ImageAsset(name: "flags/um")
internal static let un = ImageAsset(name: "flags/un")
internal static let us = ImageAsset(name: "flags/us")
internal static let uy = ImageAsset(name: "flags/uy")
internal static let uz = ImageAsset(name: "flags/uz")
internal static let va = ImageAsset(name: "flags/va")
internal static let vc = ImageAsset(name: "flags/vc")
internal static let ve = ImageAsset(name: "flags/ve")
internal static let vg = ImageAsset(name: "flags/vg")
internal static let vi = ImageAsset(name: "flags/vi")
internal static let vn = ImageAsset(name: "flags/vn")
internal static let vu = ImageAsset(name: "flags/vu")
internal static let wf = ImageAsset(name: "flags/wf")
internal static let ws = ImageAsset(name: "flags/ws")
internal static let xk = ImageAsset(name: "flags/xk")
internal static let ye = ImageAsset(name: "flags/ye")
internal static let yt = ImageAsset(name: "flags/yt")
internal static let za = ImageAsset(name: "flags/za")
internal static let zm = ImageAsset(name: "flags/zm")
internal static let zw = ImageAsset(name: "flags/zw")
}
}
internal enum Providers {
internal enum Providers {
internal static let hideme = ImageAsset(name: "providers/hideme")
internal static let mullvad = ImageAsset(name: "providers/mullvad")
internal static let nordvpn = ImageAsset(name: "providers/nordvpn")
internal static let oeck = ImageAsset(name: "providers/oeck")
internal static let pia = ImageAsset(name: "providers/pia")
internal static let placeholder = ImageAsset(name: "providers/placeholder")
internal static let protonvpn = ImageAsset(name: "providers/protonvpn")
internal static let surfshark = ImageAsset(name: "providers/surfshark")
internal static let torguard = ImageAsset(name: "providers/torguard")
internal static let tunnelbear = ImageAsset(name: "providers/tunnelbear")
internal static let vyprvpn = ImageAsset(name: "providers/vyprvpn")
internal static let windscribe = ImageAsset(name: "providers/windscribe")
}
}
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
// MARK: - Implementation Details
internal final class ColorAsset {
internal fileprivate(set) var name: String
#if os(macOS)
internal typealias Color = NSColor
#elseif os(iOS) || os(tvOS) || os(watchOS)
internal typealias Color = UIColor
#endif
@available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
internal private(set) lazy var color: Color = Color(asset: self)
#if os(iOS) || os(tvOS)
@available(iOS 11.0, tvOS 11.0, *)
internal func color(compatibleWith traitCollection: UITraitCollection) -> Color {
let bundle = BundleToken.bundle
guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else {
fatalError("Unable to load color asset named \(name).")
}
return color
}
#endif
fileprivate init(name: String) {
self.name = name
}
}
internal extension ColorAsset.Color {
@available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
convenience init!(asset: ColorAsset) {
let bundle = BundleToken.bundle
#if os(iOS) || os(tvOS)
self.init(named: asset.name, in: bundle, compatibleWith: nil)
#elseif os(macOS)
self.init(named: NSColor.Name(asset.name), bundle: bundle)
#elseif os(watchOS)
self.init(named: asset.name)
#endif
}
}
internal struct ImageAsset {
internal fileprivate(set) var name: String
#if os(macOS)
internal typealias Image = NSImage
#elseif os(iOS) || os(tvOS) || os(watchOS)
internal typealias Image = UIImage
#endif
@available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *)
internal var image: Image {
let bundle = BundleToken.bundle
#if os(iOS) || os(tvOS)
let image = Image(named: name, in: bundle, compatibleWith: nil)
#elseif os(macOS)
let name = NSImage.Name(self.name)
let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name)
#elseif os(watchOS)
let image = Image(named: name)
#endif
guard let result = image else {
fatalError("Unable to load image asset named \(name).")
}
return result
}
#if os(iOS) || os(tvOS)
@available(iOS 8.0, tvOS 9.0, *)
internal func image(compatibleWith traitCollection: UITraitCollection) -> Image {
let bundle = BundleToken.bundle
guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else {
fatalError("Unable to load image asset named \(name).")
}
return result
}
#endif
}
internal extension ImageAsset.Image {
@available(iOS 8.0, tvOS 9.0, watchOS 2.0, *)
@available(macOS, deprecated,
message: "This initializer is unsafe on macOS, please use the ImageAsset.image property")
convenience init!(asset: ImageAsset) {
#if os(iOS) || os(tvOS)
let bundle = BundleToken.bundle
self.init(named: asset.name, in: bundle, compatibleWith: nil)
#elseif os(macOS)
self.init(named: NSImage.Name(asset.name))
#elseif os(watchOS)
self.init(named: asset.name)
#endif
}
}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,394 @@
//
// Theme.swift
// Passepartout
//
// Created by Davide De Rosa on 2/24/22.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import SwiftUI
import PassepartoutCore
extension Color {
init(red: Double, green: Double, blue: Double, brightness: Double) {
self.init(
red: red * brightness,
green: green * brightness,
blue: blue * brightness
)
}
}
extension View {
@available(iOS 14, *)
func themeConfigureNavigationBarAppearance() {
let navBackgroundColor = Asset.Assets.primaryColor.color
let titleAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: Asset.Assets.lightTextColor.color
]
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.backgroundColor = navBackgroundColor
navBarAppearance.titleTextAttributes = titleAttributes
navBarAppearance.largeTitleTextAttributes = titleAttributes
let navBar = UINavigationBar.appearance()
navBar.standardAppearance = navBarAppearance
navBar.compactAppearance = navBarAppearance
navBar.scrollEdgeAppearance = navBarAppearance
// UITableView.appearance().backgroundColor = .clear
}
@available(iOS 14, *)
var themeIdiom: UIUserInterfaceIdiom {
UIDevice.current.userInterfaceIdiom
}
}
// MARK: Styles
extension View {
func themeGlobal() -> some View {
let color = themeAccentColor
return accentColor(color)
.toggleStyle(SwitchToggleStyle(tint: color))
.listStyle(.insetGrouped)
.themeNavigationViewStyle()
}
@ViewBuilder
private func themeNavigationViewStyle() -> some View {
switch UIDevice.current.userInterfaceIdiom {
case .phone:
navigationViewStyle(.stack)
default:
navigationViewStyle(.automatic)
}
}
@ViewBuilder
func themePrimaryView() -> some View {
switch UIDevice.current.userInterfaceIdiom {
case .phone:
navigationBarTitleDisplayMode(.large)
default:
themeSecondaryView()
}
}
func themeSecondaryView() -> some View {
navigationBarTitleDisplayMode(.inline)
}
func themeLongText() -> some View {
lineLimit(1)
.truncationMode(.middle)
}
}
// MARK: Colors
extension View {
var themeAccentColor: Color {
Color(Asset.Assets.accentColor.color)
}
var themePrimaryBackgroundColor: Color {
Color(Asset.Assets.primaryColor.color)
}
var themePrimaryBackground: some View {
themePrimaryBackgroundColor
.ignoresSafeArea()
}
var themeSecondaryColor: Color {
.secondary
}
var themeLightTextColor: Color {
Color(Asset.Assets.lightTextColor.color)
}
var themeErrorColor: Color {
.red
}
private func themeColor(_ string: String?, validator: (String) throws -> Void) -> Color? {
guard let string = string else {
return nil
}
do {
try validator(string)
return nil
} catch {
return themeErrorColor
}
}
}
// MARK: Fonts
extension View {
func themeDebugLogFont() -> some View {
font(.system(size: 13, weight: .medium, design: .monospaced))
}
}
// MARK: Images
extension View {
var themeAssetsLogoImage: String {
"logo"
}
func themeAssetsProviderImage(_ providerName: ProviderName) -> String {
"providers/\(providerName)"
}
func themeAssetsCountryImage(_ countryCode: String) -> String {
"flags/\(countryCode.lowercased())"
}
var themeHostImage: String {
"folder.fill"
}
var themeProviderImage: String {
"externaldrive.connected.to.line.below.fill"
}
var themeAddProfileImage: String {
"plus"
}
var themeCheckmarkImage: String {
"checkmark"
}
var themeStatusImage: String {
"network"
}
var themeShortcutsImage: String {
"mic.fill"
}
var themeDonateImage: String {
"giftcard.fill"
}
var themeRedditImage: String {
"person.3.fill"
}
var themeWriteReviewImage: String {
"heart.fill"
}
var themeAboutImage: String {
"info.circle"
}
var themeDeleteImage: String {
"trash.fill"
}
var themeRenameProfileImage: String {
"highlighter"
// "character.cursor.ibeam"
}
var themeVPNProtocolImage: String {
"bolt.fill"
// "waveform.path.ecg"
// "message.and.waveform.fill"
// "pc"
// "captions.bubble.fill"
}
var themeEndpointImage: String {
"link"
}
var themeAccountImage: String {
"person.fill"
}
var themeProviderLocationImage: String {
"location.fill"
}
var themeProviderPresetImage: String {
"slider.horizontal.3"
}
var themeProviderRefreshImage: String {
"arrow.clockwise"
}
var themeNetworkSettingsImage: String {
// "network"
"globe"
}
var themeOnDemandImage: String {
"wifi"
}
var themeDiagnosticsImage: String {
"bandage.fill"
}
var themeFAQImage: String {
"questionmark.diamond.fill"
}
var themeConceilImage: String {
"eye.slash.fill"
}
var themeRevealImage: String {
"eye.fill"
}
var themeShareImage: String {
"square.and.arrow.up"
}
func themeFavoritesImage(_ active: Bool) -> String {
active ? "bookmark.fill" : "bookmark"
}
func themeFavoriteActionImage(_ doFavorite: Bool) -> String {
doFavorite ? "bookmark" : "bookmark.slash.fill"
}
}
extension String {
var asAssetImage: Image {
Image(self)
}
var asSystemImage: Image {
Image(systemName: self)
}
}
// MARK: Shortcuts
extension View {
func themeSaveButtonLabel() -> some View {
// themeCheckmarkImage.asSystemImage
Text(L10n.Global.Strings.save)
}
func themeDoneButtonLabel() -> some View {
// themeCheckmarkImage.asSystemImage
Text(L10n.Global.Strings.ok)
}
func themeTextPicker<T: Hashable>(_ title: String, selection: Binding<T>, values: [T], description: @escaping (T) -> String) -> some View {
StyledPicker(title: title, selection: selection, values: values) {
Text(description($0))
} selectionLabel: {
Text(description($0))
.foregroundColor(themeSecondaryColor)
} listStyle: {
.insetGrouped
}
}
func themeLongContentLink(_ title: String, content: Binding<String>, withPreview preview: String? = nil) -> some View {
LongContentLink(title, content: content, preview: preview) {
Text(preview != nil ? $0 : "")
.foregroundColor(themeSecondaryColor)
}
}
@ViewBuilder
func themeErrorMessage(_ message: String?) -> some View {
if let message = message {
if message.last != "." {
Text("\(message).")
.foregroundColor(themeErrorColor)
} else {
Text(message)
.foregroundColor(themeErrorColor)
}
} else {
EmptyView()
}
}
}
// MARK: Validation
extension View {
func themeURL(_ urlString: String?) -> some View {
themeValidating(urlString, validator: Validators.url)
.keyboardType(.asciiCapable)
.autocapitalization(.none)
.disableAutocorrection(true)
}
func themeIPAddress(_ ipAddress: String?) -> some View {
themeValidating(ipAddress, validator: Validators.ipAddress)
.keyboardType(.numbersAndPunctuation)
.autocapitalization(.none)
.disableAutocorrection(true)
}
func themeSocketPort() -> some View {
keyboardType(.numberPad)
}
func themeDomainName(_ domainName: String?) -> some View {
themeValidating(domainName, validator: Validators.domainName)
.keyboardType(.asciiCapable)
.autocapitalization(.none)
.disableAutocorrection(true)
}
func themeSSID(_ text: String?) -> some View {
themeValidating(text, validator: Validators.notEmpty)
.keyboardType(.asciiCapable)
.autocapitalization(.none)
.disableAutocorrection(true)
}
private func themeValidating(_ string: String?, validator: (String) throws -> Void) -> some View {
foregroundColor(themeColor(string, validator: validator))
}
}
// MARK: Hacks
extension View {
// @available(*, deprecated, message: "mitigates multiline text truncation (1.0 does not work though)")
func xxxThemeTruncation() -> some View {
minimumScaleFactor(0.5)
}
}

View File

@ -0,0 +1,37 @@
//
// DebugLog+Constants.swift
// Passepartout
//
// Created by Davide De Rosa on 3/24/22.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import PassepartoutCore
extension DebugLog {
func decoratedString() -> String {
return decoratedString(Constants.Global.appName, Constants.Global.appVersionString)
}
func decoratedData() -> Data {
return decoratedData(Constants.Global.appName, Constants.Global.appVersionString)
}
}

View File

@ -0,0 +1,106 @@
//
// PassepartoutProviders+Extensions.swift
// Passepartout
//
// Created by Davide De Rosa on 11/4/21.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import PassepartoutCore
extension ProviderMetadata: Identifiable, Comparable, Hashable {
public var id: String {
return name
}
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.name == rhs.name
}
public static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.fullName.lowercased() < rhs.fullName.lowercased()
}
public func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
extension ProviderCategory: Comparable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.name.lowercased() == rhs.name.lowercased()
}
public static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.name.lowercased() < rhs.name.lowercased()
}
}
extension ProviderLocation: Comparable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.countryCode == rhs.countryCode
}
public static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.localizedCountry < rhs.localizedCountry
}
}
extension ProviderServer: Comparable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
// "Default" comes first
// sorts by serverIndex first, see ProtonVPN > Germany (currently "Frankfurt #203" comes before "#3")
public static func <(lhs: Self, rhs: Self) -> Bool {
if let li = lhs.serverIndex, let ri = rhs.serverIndex {
return li < ri
}
return lhs.localizedDetails < rhs.localizedDetails
}
}
extension ProviderServer.Preset: Comparable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.name == rhs.name
}
public static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.name < rhs.name
}
}
extension ProviderMetadata {
var openVPNGuidanceURL: URL? {
guard let string = Constants.URLs.openVPNGuidances[name] else {
return nil
}
return URL(string: string)
}
var referralURL: URL? {
guard let string = Constants.URLs.referrals[name] else {
return nil
}
return URL(string: string)
}
}

View File

@ -0,0 +1,45 @@
//
// TunnelKit+Identifiable.swift
// Passepartout
//
// Created by Davide De Rosa on 3/12/22.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKitCore
extension Endpoint: Identifiable {
public var id: String {
return "\(address):\(proto.port):\(proto.socketType.rawValue)"
}
}
extension IPv4Settings.Route: Identifiable {
public var id: String {
return "\(destination):\(mask):\(gateway)"
}
}
extension IPv6Settings.Route: Identifiable {
public var id: String {
return "\(destination):\(prefixLength):\(gateway)"
}
}

View File

@ -0,0 +1,44 @@
//
// VPNProtocolType+FileExtensions.swift
// Passepartout
//
// Created by Davide De Rosa on 4/7/22.
// Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
//
import Foundation
import PassepartoutCore
extension VPNProtocolType {
static let knownFileExtensions: [String] = {
let protos: [Self] = [.openVPN, .wireGuard]
return protos.map(\.fileExtension)
}()
var fileExtension: String {
switch self {
case .openVPN:
return "ovpn"
case .wireGuard:
return "wg"
}
}
}

View File

@ -2,5 +2,8 @@
"info" : { "info" : {
"author" : "xcode", "author" : "xcode",
"version" : 1 "version" : 1
},
"properties" : {
"provides-namespace" : true
} }
} }

View File

Before

Width:  |  Height:  |  Size: 960 B

After

Width:  |  Height:  |  Size: 960 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 182 B

After

Width:  |  Height:  |  Size: 182 B

View File

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 216 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 164 B

After

Width:  |  Height:  |  Size: 164 B

View File

Before

Width:  |  Height:  |  Size: 187 B

After

Width:  |  Height:  |  Size: 187 B

View File

Before

Width:  |  Height:  |  Size: 785 B

After

Width:  |  Height:  |  Size: 785 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 918 B

After

Width:  |  Height:  |  Size: 918 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 565 B

After

Width:  |  Height:  |  Size: 565 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 175 B

After

Width:  |  Height:  |  Size: 175 B

View File

Before

Width:  |  Height:  |  Size: 199 B

After

Width:  |  Height:  |  Size: 199 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 631 B

View File

Before

Width:  |  Height:  |  Size: 909 B

After

Width:  |  Height:  |  Size: 909 B

View File

Before

Width:  |  Height:  |  Size: 357 B

After

Width:  |  Height:  |  Size: 357 B

View File

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 414 B

View File

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 695 B

After

Width:  |  Height:  |  Size: 695 B

View File

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

View File

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 185 B

After

Width:  |  Height:  |  Size: 185 B

View File

Before

Width:  |  Height:  |  Size: 191 B

After

Width:  |  Height:  |  Size: 191 B

Some files were not shown because too many files have changed in this diff Show More