|
@ -0,0 +1,7 @@
|
|||
INFO_PLIST_ROOT="Passepartout/App"
|
||||
MATCH_PLATFORM="tvos"
|
||||
GYM_SCHEME="Passepartout"
|
||||
DELIVER_PLATFORM="appletvos"
|
||||
DELIVER_METADATA_PATH="Passepartout/App/fastlane/tvos/metadata"
|
||||
DELIVER_SCREENSHOTS_PATH="Passepartout/App/fastlane/tvos/screenshots"
|
||||
CHANGELOG="CHANGELOG.md"
|
|
@ -17,7 +17,7 @@ env:
|
|||
jobs:
|
||||
build_upload:
|
||||
name: Distribute Private Beta
|
||||
runs-on: macos-12
|
||||
runs-on: macos-13
|
||||
environment:
|
||||
name: private_beta
|
||||
strategy:
|
||||
|
@ -50,7 +50,7 @@ jobs:
|
|||
go-version: "^1.17"
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
xcode-version: "15.1"
|
||||
- name: Create keychain
|
||||
uses: ./.github/actions/create-keychain
|
||||
with:
|
||||
|
|
|
@ -16,7 +16,7 @@ env:
|
|||
jobs:
|
||||
build_upload:
|
||||
name: Upload to ASC
|
||||
runs-on: macos-12
|
||||
runs-on: macos-13
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
|
@ -48,7 +48,7 @@ jobs:
|
|||
go-version: "^1.17"
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
xcode-version: "15.1"
|
||||
- name: Store app version
|
||||
id: app_version
|
||||
if: ${{ matrix.use_version }}
|
||||
|
|
|
@ -18,7 +18,7 @@ concurrency:
|
|||
jobs:
|
||||
run_tests:
|
||||
name: Run tests
|
||||
runs-on: macos-12
|
||||
runs-on: macos-13
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
|||
submodules: true
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
xcode-version: "15.1"
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
|
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- App for tvOS. [#315](https://github.com/passepartoutvpn/passepartout-apple/issues/315)
|
||||
- WireGuard: Show data count. [#312](https://github.com/passepartoutvpn/passepartout-apple/issues/312)
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 55;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -19,7 +19,7 @@
|
|||
0E0838FA2877325A00A34EC0 /* LightProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0838F92877325A00A34EC0 /* LightProviderManager.swift */; };
|
||||
0E0838FB2877325A00A34EC0 /* LightProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0838F92877325A00A34EC0 /* LightProviderManager.swift */; };
|
||||
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0838FC2877334300A34EC0 /* DefaultLightProviderManager.swift */; };
|
||||
0E09E35D2834172800BE1BAE /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 0E09E35C2834172800BE1BAE /* Credits.rtf */; };
|
||||
0E09E35D2834172800BE1BAE /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 0E09E35C2834172800BE1BAE /* Credits.rtf */; platformFilter = maccatalyst; };
|
||||
0E0BD27327B2EA2C00583AC5 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0BD27227B2EA2C00583AC5 /* MainView.swift */; };
|
||||
0E0BD27627B2EB2200583AC5 /* DonateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0BD27527B2EB2200583AC5 /* DonateView.swift */; };
|
||||
0E0BD27927B2EBE500583AC5 /* ShortcutsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0BD27827B2EBE500583AC5 /* ShortcutsView.swift */; };
|
||||
|
@ -30,6 +30,7 @@
|
|||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6529C84CF60022E884 /* LogoView.swift */; };
|
||||
0E1AD5CE2A268645002AE6E6 /* Errors+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */; };
|
||||
0E1B5F5C29C506AD00FE7D18 /* DiagnosticsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */; };
|
||||
0E1DC1BF2B3618EE008B755E /* ProfileView+TV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1DC1BE2B3618EE008B755E /* ProfileView+TV.swift */; };
|
||||
0E1F5628287F0ECB00F8ADD7 /* ProviderProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */; };
|
||||
0E1F562B287F0EF100F8ADD7 /* ProviderProfileItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */; };
|
||||
0E293857285A73BC002A6E0E /* AppContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E293856285A73BC002A6E0E /* AppContext+Shared.swift */; };
|
||||
|
@ -47,6 +48,9 @@
|
|||
0E2E0B762B335AAB00E3204A /* UpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */; };
|
||||
0E2E0B772B335AAB00E3204A /* UpgradeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */; };
|
||||
0E2E0B782B335AAB00E3204A /* PersistenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */; };
|
||||
0E330F532B30469700930C7C /* MockProfileRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E330F522B30469700930C7C /* MockProfileRepository.swift */; };
|
||||
0E330F552B30946600930C7C /* ActiveProfileView+TV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E330F542B30946600930C7C /* ActiveProfileView+TV.swift */; };
|
||||
0E330F572B30952300930C7C /* ProfilesList+TV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E330F562B30952300930C7C /* ProfilesList+TV.swift */; };
|
||||
0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */; };
|
||||
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */; };
|
||||
0E34A2CF27CADA6300C73B67 /* GenericVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */; };
|
||||
|
@ -108,6 +112,7 @@
|
|||
0E7A8C0C2A1D4A6100780F4B /* PassepartoutLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 0E7A8C0B2A1D4A6100780F4B /* PassepartoutLibrary */; };
|
||||
0E7A8C0F2A1D54DE00780F4B /* Picker+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */; };
|
||||
0E7A8C102A1D54DE00780F4B /* Picker+Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */; };
|
||||
0E859B832B2EE08700F80D92 /* OrganizerView+TV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E859B822B2EE08700F80D92 /* OrganizerView+TV.swift */; };
|
||||
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */; };
|
||||
0E92D7C627F103300033CB7B /* ProfileView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */; };
|
||||
0E92D7C927F1042A0033CB7B /* ProfileView+Extra.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */; };
|
||||
|
@ -184,10 +189,12 @@
|
|||
0ED89C1727DE0E05008B36D6 /* IntentEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1627DE0E05008B36D6 /* IntentEditView.swift */; };
|
||||
0ED89C1C27DE3ABC008B36D6 /* ShortcutsView+Add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */; };
|
||||
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1D27DE3F8D008B36D6 /* IntentAddView.swift */; };
|
||||
0EDDEC7D28D0DC140017802E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EDDEC7C28D0DC130017802E /* LaunchScreen.storyboard */; };
|
||||
0EDE02C227F61C79000FBE3C /* EditableTextList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE02C127F61C79000FBE3C /* EditableTextList.swift */; };
|
||||
0EE11CD2280D8317003BE431 /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE11CD1280D8317003BE431 /* SettingsButton.swift */; };
|
||||
0EE562782B2EE3EC000C52F6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EE562772B2EE3EC000C52F6 /* LaunchScreen.storyboard */; platformFilters = (ios, maccatalyst, ); };
|
||||
0EE79B2F2B2ED99500C1220C /* MainView+TV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE79B2E2B2ED99500C1220C /* MainView+TV.swift */; };
|
||||
0EE8B7E327FF340F00B68621 /* VPNProtocolType+FileExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE8B7E227FF340F00B68621 /* VPNProtocolType+FileExtensions.swift */; };
|
||||
0EED5B9D2B3700AB009D1E97 /* TunnelError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF656402B36C00E00CEFC96 /* TunnelError.swift */; };
|
||||
0EF0FAF627DD0211007EB181 /* PaywallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF0FAF527DD0211007EB181 /* PaywallView.swift */; };
|
||||
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA591122733DD4E0096F796 /* IntentDispatcher.swift */; };
|
||||
0EF0FAF927DD212C007EB181 /* IntentActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF0FAF827DD212C007EB181 /* IntentActivity.swift */; };
|
||||
|
@ -195,6 +202,10 @@
|
|||
0EF2212D27E66EB5001D0BD7 /* AddProviderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF2212C27E66EB5001D0BD7 /* AddProviderView.swift */; };
|
||||
0EF2212F27E66F60001D0BD7 /* AddProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF2212E27E66F60001D0BD7 /* AddProfileView.swift */; };
|
||||
0EF2213127E674BD001D0BD7 /* AddProviderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF2213027E674BD001D0BD7 /* AddProviderViewModel.swift */; };
|
||||
0EF6563E2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF6563D2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift */; };
|
||||
0EF6563F2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF6563D2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift */; };
|
||||
0EF656422B36C01200CEFC96 /* TunnelError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF656402B36C00E00CEFC96 /* TunnelError.swift */; };
|
||||
0EF656432B36C01200CEFC96 /* TunnelError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF656402B36C00E00CEFC96 /* TunnelError.swift */; };
|
||||
0EF8C5A828213C510053CE89 /* OrganizerView+Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF8C5A728213C510053CE89 /* OrganizerView+Profiles.swift */; };
|
||||
A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38D607628AFCFD20005C271 /* SettingsView.swift */; };
|
||||
A3A7CC462878DC8300172D7D /* ProviderServerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A7CC452878DC8300172D7D /* ProviderServerItem.swift */; };
|
||||
|
@ -248,6 +259,13 @@
|
|||
remoteGlobalIDString = 0ECF71F327B6D9CD00CDB528;
|
||||
remoteInfo = WireGuardGo;
|
||||
};
|
||||
0EE79B342B2EDB9C00C1220C /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 0E57F63020C83FC5008323CF /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0EE79B302B2EDB5D00C1220C;
|
||||
remoteInfo = WireGuardGoTV;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -311,6 +329,7 @@
|
|||
0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Errors+L10n.swift"; sourceTree = "<group>"; };
|
||||
0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticsSection.swift; sourceTree = "<group>"; };
|
||||
0E1C0A52238FFF97009FC087 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
0E1DC1BE2B3618EE008B755E /* ProfileView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+TV.swift"; sourceTree = "<group>"; };
|
||||
0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileItem.swift; sourceTree = "<group>"; };
|
||||
0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProviderProfileItem+ViewModel.swift"; sourceTree = "<group>"; };
|
||||
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
|
@ -329,6 +348,9 @@
|
|||
0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
||||
0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpgradeManager.swift; sourceTree = "<group>"; };
|
||||
0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistenceManager.swift; sourceTree = "<group>"; };
|
||||
0E330F522B30469700930C7C /* MockProfileRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProfileRepository.swift; sourceTree = "<group>"; };
|
||||
0E330F542B30946600930C7C /* ActiveProfileView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActiveProfileView+TV.swift"; sourceTree = "<group>"; };
|
||||
0E330F562B30952300930C7C /* ProfilesList+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfilesList+TV.swift"; sourceTree = "<group>"; };
|
||||
0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenVPN+L10n.swift"; sourceTree = "<group>"; };
|
||||
0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Core+L10n.swift"; sourceTree = "<group>"; };
|
||||
0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericVersionView.swift; sourceTree = "<group>"; };
|
||||
|
@ -391,6 +413,7 @@
|
|||
0E7577DE2817E22C00081CBE /* VPNToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNToggle.swift; sourceTree = "<group>"; };
|
||||
0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+OpenVPN.swift"; sourceTree = "<group>"; };
|
||||
0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+Network.swift"; sourceTree = "<group>"; };
|
||||
0E859B822B2EE08700F80D92 /* OrganizerView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+TV.swift"; sourceTree = "<group>"; };
|
||||
0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostViewModel.swift; sourceTree = "<group>"; };
|
||||
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Configuration.swift"; sourceTree = "<group>"; };
|
||||
0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Extra.swift"; sourceTree = "<group>"; };
|
||||
|
@ -493,13 +516,14 @@
|
|||
0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShortcutsView+Add.swift"; sourceTree = "<group>"; };
|
||||
0ED89C1D27DE3F8D008B36D6 /* IntentAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentAddView.swift; sourceTree = "<group>"; };
|
||||
0EDCEF692B337BEB0023A7FF /* PassepartoutLibrary.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PassepartoutLibrary.xctestplan; sourceTree = "<group>"; };
|
||||
0EDDEC7C28D0DC130017802E /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
0EDE02C127F61C79000FBE3C /* EditableTextList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTextList.swift; sourceTree = "<group>"; };
|
||||
0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PassepartoutOpenVPNTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0EDE8DC320C86910004C739C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0EDE8DD220C86978004C739C /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; };
|
||||
0EDE8DE220C86A13004C739C /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
|
||||
0EE11CD1280D8317003BE431 /* SettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsButton.swift; sourceTree = "<group>"; };
|
||||
0EE562772B2EE3EC000C52F6 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
0EE79B2E2B2ED99500C1220C /* MainView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainView+TV.swift"; sourceTree = "<group>"; };
|
||||
0EE8B7E227FF340F00B68621 /* VPNProtocolType+FileExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VPNProtocolType+FileExtensions.swift"; sourceTree = "<group>"; };
|
||||
0EF0FAF527DD0211007EB181 /* PaywallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallView.swift; sourceTree = "<group>"; };
|
||||
0EF0FAF827DD212C007EB181 /* IntentActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentActivity.swift; sourceTree = "<group>"; };
|
||||
|
@ -507,6 +531,8 @@
|
|||
0EF2212C27E66EB5001D0BD7 /* AddProviderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProviderView.swift; sourceTree = "<group>"; };
|
||||
0EF2212E27E66F60001D0BD7 /* AddProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProfileView.swift; sourceTree = "<group>"; };
|
||||
0EF2213027E674BD001D0BD7 /* AddProviderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProviderViewModel.swift; sourceTree = "<group>"; };
|
||||
0EF6563D2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEPacketTunnelProvider+Expiration.swift"; sourceTree = "<group>"; };
|
||||
0EF656402B36C00E00CEFC96 /* TunnelError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelError.swift; sourceTree = "<group>"; };
|
||||
0EF8C5A728213C510053CE89 /* OrganizerView+Profiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrganizerView+Profiles.swift"; sourceTree = "<group>"; };
|
||||
A373484D29DC4F4500D1613C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A373484E29DC504000D1613C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
@ -572,6 +598,7 @@
|
|||
0E021D9B284E68580077EF5D /* AppContext.swift */,
|
||||
0E293856285A73BC002A6E0E /* AppContext+Shared.swift */,
|
||||
0E021D9A284E68580077EF5D /* CoreContext.swift */,
|
||||
0E330F522B30469700930C7C /* MockProfileRepository.swift */,
|
||||
);
|
||||
path = Context;
|
||||
sourceTree = "<group>";
|
||||
|
@ -630,6 +657,7 @@
|
|||
0E35C0AE280EF8A80071FA35 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EE79B2D2B2ED96D00C1220C /* TV */,
|
||||
0E44689B27B11B5300A14CE4 /* AboutView.swift */,
|
||||
0ECF71ED27B6A99300CDB528 /* AccountView.swift */,
|
||||
0E039278281890B100827C10 /* AddHostView.swift */,
|
||||
|
@ -673,6 +701,7 @@
|
|||
0E3CD482280DAE92007075C0 /* ProfileView+MainMenu.swift */,
|
||||
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */,
|
||||
0EBC074B27EB673C00208AD9 /* ProfileView+Rename.swift */,
|
||||
0E1DC1BE2B3618EE008B755E /* ProfileView+TV.swift */,
|
||||
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */,
|
||||
0E71ACF027C1073800F85C4B /* ProviderLocationView.swift */,
|
||||
0E71ACEE27C106B400F85C4B /* ProviderPresetView.swift */,
|
||||
|
@ -889,7 +918,7 @@
|
|||
0E57F64720C83FC7008323CF /* Info.plist */,
|
||||
0E0C072B236087A100155AAC /* InfoPlist.strings */,
|
||||
0E9E5AE227B44CF1008C95DA /* Localizable.strings */,
|
||||
0EDDEC7C28D0DC130017802E /* LaunchScreen.storyboard */,
|
||||
0EE562772B2EE3EC000C52F6 /* LaunchScreen.storyboard */,
|
||||
0E3FC6852867A3F9009B851C /* AppDelegate.swift */,
|
||||
0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */,
|
||||
0E0F4C5929C761850022E884 /* SceneDelegate.swift */,
|
||||
|
@ -975,6 +1004,8 @@
|
|||
0ED2B33D27D3C53400FD8EA9 /* WireGuard */,
|
||||
0ED31C3B20CF39510027975F /* Tunnel.entitlements */,
|
||||
0EDE8DC320C86910004C739C /* Info.plist */,
|
||||
0EF6563D2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift */,
|
||||
0EF656402B36C00E00CEFC96 /* TunnelError.swift */,
|
||||
);
|
||||
path = Tunnel;
|
||||
sourceTree = "<group>";
|
||||
|
@ -987,6 +1018,17 @@
|
|||
name = Packages;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0EE79B2D2B2ED96D00C1220C /* TV */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E330F542B30946600930C7C /* ActiveProfileView+TV.swift */,
|
||||
0EE79B2E2B2ED99500C1220C /* MainView+TV.swift */,
|
||||
0E859B822B2EE08700F80D92 /* OrganizerView+TV.swift */,
|
||||
0E330F562B30952300930C7C /* ProfilesList+TV.swift */,
|
||||
);
|
||||
path = TV;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
374B9F085E1148C37CF9117A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1014,6 +1056,20 @@
|
|||
passBuildSettingsInEnvironment = 1;
|
||||
productName = PassepartoutWireGuard;
|
||||
};
|
||||
0EE79B302B2EDB5D00C1220C /* WireGuardGoTV */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = "$(ACTION)";
|
||||
buildConfigurationList = 0EE79B312B2EDB5D00C1220C /* Build configuration list for PBXLegacyTarget "WireGuardGoTV" */;
|
||||
buildPhases = (
|
||||
);
|
||||
buildToolPath = "$(PROJECT_DIR)/Passepartout/App/Scripts/build_wireguard_go_bridge.sh";
|
||||
buildWorkingDirectory = "";
|
||||
dependencies = (
|
||||
);
|
||||
name = WireGuardGoTV;
|
||||
passBuildSettingsInEnvironment = 1;
|
||||
productName = PassepartoutWireGuard;
|
||||
};
|
||||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -1052,9 +1108,10 @@
|
|||
dependencies = (
|
||||
0ECB78E7285F5CC400B0E460 /* PBXTargetDependency */,
|
||||
0E41BDAB286713F6006346B4 /* PBXTargetDependency */,
|
||||
0ECF71FC27B6DA6700CDB528 /* PBXTargetDependency */,
|
||||
0EB2B14A2733FB6F007705AB /* PBXTargetDependency */,
|
||||
0ED2B36227D3C99100FD8EA9 /* PBXTargetDependency */,
|
||||
0ECF71FC27B6DA6700CDB528 /* PBXTargetDependency */,
|
||||
0EE79B352B2EDB9C00C1220C /* PBXTargetDependency */,
|
||||
);
|
||||
name = Passepartout;
|
||||
packageProductDependencies = (
|
||||
|
@ -1210,8 +1267,9 @@
|
|||
0ECB78D9285F52F700B0E460 /* PassepartoutMac */,
|
||||
0E41BD96286711C3006346B4 /* PassepartoutLauncher */,
|
||||
0EDE8DBE20C86910004C739C /* OpenVPNTunnel */,
|
||||
0ECF71F327B6D9CD00CDB528 /* WireGuardGo */,
|
||||
0ED2B33E27D3C77800FD8EA9 /* WireGuardTunnel */,
|
||||
0ECF71F327B6D9CD00CDB528 /* WireGuardGo */,
|
||||
0EE79B302B2EDB5D00C1220C /* WireGuardGoTV */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -1229,8 +1287,8 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E6059CB27FCC5DE003F4063 /* Flags.xcassets in Resources */,
|
||||
0EE562782B2EE3EC000C52F6 /* LaunchScreen.storyboard in Resources */,
|
||||
0E0C0729236087A100155AAC /* InfoPlist.strings in Resources */,
|
||||
0EDDEC7D28D0DC140017802E /* LaunchScreen.storyboard in Resources */,
|
||||
0E6059CC27FCC5DE003F4063 /* Providers.xcassets in Resources */,
|
||||
0E6059CD27FCC5DE003F4063 /* Assets.xcassets in Resources */,
|
||||
0E9E5AEF27B44CF1008C95DA /* Localizable.strings in Resources */,
|
||||
|
@ -1376,6 +1434,7 @@
|
|||
0E2DE71C27DCCFE80067B9E1 /* TunnelKit+Extensions.swift in Sources */,
|
||||
0ED1D6DE27DBA42100983466 /* DiagnosticsView+WireGuard.swift in Sources */,
|
||||
0EF2213127E674BD001D0BD7 /* AddProviderViewModel.swift in Sources */,
|
||||
0EED5B9D2B3700AB009D1E97 /* TunnelError.swift in Sources */,
|
||||
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */,
|
||||
0E3A593C2A50975700B3FE40 /* ErrorHandler.swift in Sources */,
|
||||
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */,
|
||||
|
@ -1411,6 +1470,7 @@
|
|||
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */,
|
||||
0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */,
|
||||
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */,
|
||||
0E330F552B30946600930C7C /* ActiveProfileView+TV.swift in Sources */,
|
||||
0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */,
|
||||
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */,
|
||||
0ED30DCF27EA1EF80057D8A3 /* PaywallView+Restricted.swift in Sources */,
|
||||
|
@ -1421,6 +1481,7 @@
|
|||
0E96D30228720067005EFBCF /* LightVPNManager.swift in Sources */,
|
||||
0ED89C1727DE0E05008B36D6 /* IntentEditView.swift in Sources */,
|
||||
0E70589B28377DC40075D1D2 /* VPNStatusText.swift in Sources */,
|
||||
0EE79B2F2B2ED99500C1220C /* MainView+TV.swift in Sources */,
|
||||
0E71ACE927C1055300F85C4B /* NetworkSettingsView.swift in Sources */,
|
||||
0EB34BCA27C6A70200B126DA /* OnDemandView.swift in Sources */,
|
||||
0E0838FA2877325A00A34EC0 /* LightProviderManager.swift in Sources */,
|
||||
|
@ -1432,6 +1493,7 @@
|
|||
0EB17EBA27D2560300D473B5 /* PassepartoutProviders+Extensions.swift in Sources */,
|
||||
0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */,
|
||||
0E2E0B6F2B335A8E00E3204A /* AppPreference.swift in Sources */,
|
||||
0E859B832B2EE08700F80D92 /* OrganizerView+TV.swift in Sources */,
|
||||
0E5468062867AEC500F74D1C /* MacMenu.swift in Sources */,
|
||||
0E71ACE327C0F2E400F85C4B /* Providers+L10n.swift in Sources */,
|
||||
0E2E0B752B335AAB00E3204A /* IntentsManager.swift in Sources */,
|
||||
|
@ -1457,6 +1519,7 @@
|
|||
0E2A8D4927ADF87F00207D04 /* PassepartoutApp.swift in Sources */,
|
||||
0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */,
|
||||
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */,
|
||||
0E330F572B30952300930C7C /* ProfilesList+TV.swift in Sources */,
|
||||
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */,
|
||||
0E2E0B772B335AAB00E3204A /* UpgradeManager.swift in Sources */,
|
||||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */,
|
||||
|
@ -1471,6 +1534,7 @@
|
|||
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */,
|
||||
0E2E0B782B335AAB00E3204A /* PersistenceManager.swift in Sources */,
|
||||
0EF8C5A828213C510053CE89 /* OrganizerView+Profiles.swift in Sources */,
|
||||
0E330F532B30469700930C7C /* MockProfileRepository.swift in Sources */,
|
||||
0E3CD483280DAE92007075C0 /* ProfileView+MainMenu.swift in Sources */,
|
||||
0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */,
|
||||
0E293857285A73BC002A6E0E /* AppContext+Shared.swift in Sources */,
|
||||
|
@ -1478,6 +1542,7 @@
|
|||
0EB4042E27CA136300378B1A /* AddingTextField.swift in Sources */,
|
||||
0EE8B7E327FF340F00B68621 /* VPNProtocolType+FileExtensions.swift in Sources */,
|
||||
0EF2212B27E667EA001D0BD7 /* AddProviderView+Name.swift in Sources */,
|
||||
0E1DC1BF2B3618EE008B755E /* ProfileView+TV.swift in Sources */,
|
||||
0E065F112813269500062CAF /* WelcomeView.swift in Sources */,
|
||||
0E2DE71F27DCD0290067B9E1 /* TunnelKit+L10n.swift in Sources */,
|
||||
0E49F6BF27D764AF00385834 /* EndpointAdvancedView.swift in Sources */,
|
||||
|
@ -1500,9 +1565,11 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EF6563F2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift in Sources */,
|
||||
0ED2B35B27D3C94F00FD8EA9 /* PacketTunnelProvider.swift in Sources */,
|
||||
0ED30DDD27EA35230057D8A3 /* Constants.swift in Sources */,
|
||||
0ED1A5FD2B2B98CC00A0EA90 /* Constants+Tunnel.swift in Sources */,
|
||||
0EF656432B36C01200CEFC96 /* TunnelError.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1510,9 +1577,11 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EF6563E2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift in Sources */,
|
||||
0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */,
|
||||
0EB17EA727D226B400D473B5 /* Constants.swift in Sources */,
|
||||
0ED30DDB27EA351C0057D8A3 /* Constants+Tunnel.swift in Sources */,
|
||||
0EF656422B36C01200CEFC96 /* TunnelError.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1538,6 +1607,10 @@
|
|||
};
|
||||
0ECF71FC27B6DA6700CDB528 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilters = (
|
||||
ios,
|
||||
maccatalyst,
|
||||
);
|
||||
target = 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */;
|
||||
targetProxy = 0ECF71FB27B6DA6700CDB528 /* PBXContainerItemProxy */;
|
||||
};
|
||||
|
@ -1551,6 +1624,14 @@
|
|||
target = 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */;
|
||||
targetProxy = 0ED2B36A27D3CAB100FD8EA9 /* PBXContainerItemProxy */;
|
||||
};
|
||||
0EE79B352B2EDB9C00C1220C /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilters = (
|
||||
tvos,
|
||||
);
|
||||
target = 0EE79B302B2EDB5D00C1220C /* WireGuardGoTV */;
|
||||
targetProxy = 0EE79B342B2EDB9C00C1220C /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
|
@ -1730,7 +1811,8 @@
|
|||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3";
|
||||
TVOS_DEPLOYMENT_TARGET = 17.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -1791,7 +1873,8 @@
|
|||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3";
|
||||
TVOS_DEPLOYMENT_TARGET = 17.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -1801,6 +1884,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
"ASSETCATALOG_COMPILER_APPICON_NAME[sdk=appletvos*]" = TV;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
|
||||
|
@ -1814,11 +1898,12 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID)";
|
||||
PRODUCT_NAME = Passepartout;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout tvos";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout catalyst";
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_STRICT_CONCURRENCY = targeted;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -1827,6 +1912,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
"ASSETCATALOG_COMPILER_APPICON_NAME[sdk=appletvos*]" = TV;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
|
||||
|
@ -1840,10 +1926,11 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID)";
|
||||
PRODUCT_NAME = Passepartout;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout tvos";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout catalyst";
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_STRICT_CONCURRENCY = targeted;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -1959,8 +2046,10 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).WireGuardTunnel";
|
||||
PRODUCT_NAME = PassepartoutWireGuardTunnel;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel tvos";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel catalyst";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -1979,8 +2068,10 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).WireGuardTunnel";
|
||||
PRODUCT_NAME = PassepartoutWireGuardTunnel;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel tvos";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel catalyst";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -1999,8 +2090,10 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).OpenVPNTunnel";
|
||||
PRODUCT_NAME = PassepartoutOpenVPNTunnel;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel tvos";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel catalyst";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -2019,12 +2112,51 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).OpenVPNTunnel";
|
||||
PRODUCT_NAME = PassepartoutOpenVPNTunnel;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel tvos";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel catalyst";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0EE79B322B2EDB5D00C1220C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUGGING_SYMBOLS = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0EE79B332B2EDB5D00C1220C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = DTDYD63ZX9;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
|
@ -2091,6 +2223,15 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0EE79B312B2EDB5D00C1220C /* Build configuration list for PBXLegacyTarget "WireGuardGoTV" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0EE79B322B2EDB5D00C1220C /* Debug */,
|
||||
0EE79B332B2EDB5D00C1220C /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
|
|
|
@ -1,70 +1,67 @@
|
|||
{
|
||||
"object": {
|
||||
"pins" : [
|
||||
{
|
||||
"package": "DTFoundation",
|
||||
"repositoryURL": "https://github.com/Cocoanetics/DTFoundation.git",
|
||||
"identity" : "dtfoundation",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Cocoanetics/DTFoundation.git",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision" : "76062513434421cb6c8a1ae1d4f8368a7ebc2da3",
|
||||
"version" : "1.7.18"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "GenericJSON",
|
||||
"repositoryURL": "https://github.com/zoul/generic-json-swift",
|
||||
"identity" : "generic-json-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zoul/generic-json-swift",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision" : "0a06575f4038b504e78ac330913d920f1630f510",
|
||||
"version" : "2.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Kvitto",
|
||||
"repositoryURL": "https://github.com/Cocoanetics/Kvitto",
|
||||
"identity" : "kvitto",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Cocoanetics/Kvitto",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision" : "88888674d772ddcf19671159ed0022cb0bc37be2",
|
||||
"version" : "1.0.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "openssl-apple",
|
||||
"repositoryURL": "https://github.com/passepartoutvpn/openssl-apple",
|
||||
"identity" : "openssl-apple",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/passepartoutvpn/openssl-apple",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision" : "026702febcaebcbf9ea68f2fa66b017eba998cdf",
|
||||
"version" : "3.2.105"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftyBeaver",
|
||||
"repositoryURL": "https://github.com/SwiftyBeaver/SwiftyBeaver",
|
||||
"identity" : "swiftybeaver",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SwiftyBeaver/SwiftyBeaver",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision" : "12b5acf96d98f91d50de447369bd18df74600f1a",
|
||||
"version" : "1.9.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "TunnelKit",
|
||||
"repositoryURL": "https://github.com/passepartoutvpn/tunnelkit",
|
||||
"identity" : "tunnelkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/passepartoutvpn/tunnelkit",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision": "bda84bf569792fbb702d0173de3c9c58768f9153",
|
||||
"version": null
|
||||
"revision" : "708c785e615f5715ce08386c772c92fb45730a3a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "WireGuardKit",
|
||||
"repositoryURL": "https://github.com/passepartoutvpn/wireguard-apple",
|
||||
"identity" : "wireguard-apple",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/passepartoutvpn/wireguard-apple",
|
||||
"state" : {
|
||||
"branch": null,
|
||||
"revision": "73d9152fa0cb661db0348a1ac11dbbf998422a50",
|
||||
"version": "1.0.17"
|
||||
"branch" : "develop",
|
||||
"revision" : "b79f0f150356d8200a64922ecf041dd020140aa0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@
|
|||
<EnvironmentVariable
|
||||
key = "APP_TYPE"
|
||||
value = "2"
|
||||
isEnabled = "YES">
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "LOG_LEVEL"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.com.algoritmico.Passepartout</string>
|
||||
<string>iCloud.com.algoritmico.Passepartout.Shared</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
|
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "tv"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"layers" : [
|
||||
{
|
||||
"filename" : "Front.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Back.imagestacklayer"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "tv"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 8.3 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon@2x.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"layers" : [
|
||||
{
|
||||
"filename" : "Front.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Back.imagestacklayer"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "AppIcon@2x.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"assets" : [
|
||||
{
|
||||
"filename" : "App Icon - App Store.imagestack",
|
||||
"idiom" : "tv",
|
||||
"role" : "primary-app-icon",
|
||||
"size" : "1280x768"
|
||||
},
|
||||
{
|
||||
"filename" : "App Icon.imagestack",
|
||||
"idiom" : "tv",
|
||||
"role" : "primary-app-icon",
|
||||
"size" : "400x240"
|
||||
},
|
||||
{
|
||||
"filename" : "Top Shelf Image Wide.imageset",
|
||||
"idiom" : "tv",
|
||||
"role" : "top-shelf-image-wide",
|
||||
"size" : "2320x720"
|
||||
},
|
||||
{
|
||||
"filename" : "Top Shelf Image.imageset",
|
||||
"idiom" : "tv",
|
||||
"role" : "top-shelf-image",
|
||||
"size" : "1920x720"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
18
Passepartout/App/Assets.xcassets/TV.brandassets/Top Shelf Image Wide.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "TopShelf.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "TopShelf@2x.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Passepartout/App/Assets.xcassets/TV.brandassets/Top Shelf Image Wide.imageset/TopShelf.png
vendored
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
Passepartout/App/Assets.xcassets/TV.brandassets/Top Shelf Image Wide.imageset/TopShelf@2x.png
vendored
Normal file
After Width: | Height: | Size: 118 KiB |
18
Passepartout/App/Assets.xcassets/TV.brandassets/Top Shelf Image.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "TopShelf.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "TopShelf@2x.png",
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Passepartout/App/Assets.xcassets/TV.brandassets/Top Shelf Image.imageset/TopShelf.png
vendored
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
Passepartout/App/Assets.xcassets/TV.brandassets/Top Shelf Image.imageset/TopShelf@2x.png
vendored
Normal file
After Width: | Height: | Size: 99 KiB |
|
@ -44,6 +44,8 @@ extension Constants {
|
|||
enum CloudKit {
|
||||
static let containerId: String = bundleConfig("cloudkit_id")
|
||||
|
||||
static let sharedContainerId: String = bundleConfig("cloudkit_shared_id")
|
||||
|
||||
static let coreDataZone = "com.apple.coredata.cloudkit.zone"
|
||||
}
|
||||
|
||||
|
@ -84,6 +86,8 @@ extension Constants {
|
|||
return []
|
||||
}
|
||||
#endif
|
||||
|
||||
static let tvLimitedMinutes = 10
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +135,8 @@ extension Constants {
|
|||
enum Persistence {
|
||||
static let profilesContainerName = "Profiles"
|
||||
|
||||
static let sharedProfilesContainerName = "SharedProfiles"
|
||||
|
||||
static let providersContainerName = "Providers"
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import LocalAuthentication
|
||||
#endif
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -33,20 +35,28 @@ extension View {
|
|||
}
|
||||
|
||||
var themeIsiPadPortrait: Bool {
|
||||
#if !os(tvOS)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
false
|
||||
#else
|
||||
let device: UIDevice = .current
|
||||
return device.userInterfaceIdiom == .pad && device.orientation.isPortrait
|
||||
#endif
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
}
|
||||
|
||||
var themeIsiPadMultitasking: Bool {
|
||||
#if !os(tvOS)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
false
|
||||
#else
|
||||
UIDevice.current.userInterfaceIdiom == .pad
|
||||
#endif
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +65,7 @@ extension View {
|
|||
extension View {
|
||||
func themeGlobal() -> some View {
|
||||
themeNavigationViewStyle()
|
||||
#if !os(tvOS)
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
.themeLockScreen()
|
||||
#endif
|
||||
|
@ -62,22 +73,39 @@ extension View {
|
|||
.listStyle(themeListStyleValue())
|
||||
.toggleStyle(themeToggleStyleValue())
|
||||
.menuStyle(.borderlessButton)
|
||||
#endif
|
||||
.withErrorHandler()
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
func themeTV() -> some View {
|
||||
GeometryReader { geo in
|
||||
self
|
||||
.padding(.horizontal, 0.25 * geo.size.width)
|
||||
.scrollClipDisabled()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
func themePrimaryView() -> some View {
|
||||
#if targetEnvironment(macCatalyst)
|
||||
navigationBarTitleDisplayMode(.inline)
|
||||
.themeSidebarListStyle()
|
||||
#else
|
||||
#elseif !os(tvOS)
|
||||
navigationBarTitleDisplayMode(.large)
|
||||
.navigationTitle(Unlocalized.appName)
|
||||
.themeSidebarListStyle()
|
||||
#else
|
||||
self
|
||||
#endif
|
||||
}
|
||||
|
||||
func themeSecondaryView() -> some View {
|
||||
#if !os(tvOS)
|
||||
navigationBarTitleDisplayMode(.inline)
|
||||
#else
|
||||
self
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -93,6 +121,7 @@ extension View {
|
|||
|
||||
@ViewBuilder
|
||||
private func themeSidebarListStyle() -> some View {
|
||||
#if !os(tvOS)
|
||||
switch themeIdiom {
|
||||
case .phone:
|
||||
listStyle(.insetGrouped)
|
||||
|
@ -100,6 +129,9 @@ extension View {
|
|||
default:
|
||||
listStyle(.sidebar)
|
||||
}
|
||||
#else
|
||||
self
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -108,11 +140,19 @@ extension View {
|
|||
}
|
||||
|
||||
private func themeListStyleValue() -> some ListStyle {
|
||||
#if !os(tvOS)
|
||||
.insetGrouped
|
||||
#else
|
||||
PlainListStyle()
|
||||
#endif
|
||||
}
|
||||
|
||||
private func themeToggleStyleValue() -> some ToggleStyle {
|
||||
#if !os(tvOS)
|
||||
.switch
|
||||
#else
|
||||
DefaultToggleStyle()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +219,10 @@ extension View {
|
|||
"eye"
|
||||
}
|
||||
|
||||
var themeAppleTVImage: String {
|
||||
"tv"
|
||||
}
|
||||
|
||||
// MARK: Organizer
|
||||
|
||||
func themeAssetsProviderImage(_ providerName: ProviderName) -> String {
|
||||
|
@ -387,6 +431,7 @@ extension View {
|
|||
|
||||
// MARK: Shortcuts
|
||||
|
||||
#if !os(tvOS)
|
||||
extension ShortcutType {
|
||||
var themeImageName: String {
|
||||
switch self {
|
||||
|
@ -401,6 +446,7 @@ extension ShortcutType {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: Animations
|
||||
|
||||
|
@ -497,6 +543,7 @@ extension View {
|
|||
|
||||
// MARK: Lock screen
|
||||
|
||||
#if !os(tvOS)
|
||||
extension View {
|
||||
func themeLockScreen() -> some View {
|
||||
@AppStorage(AppPreference.locksInBackground.key) var locksInBackground = false
|
||||
|
@ -528,6 +575,7 @@ extension View {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: Validation
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ final class AppContext {
|
|||
persistenceManager = PersistenceManager(
|
||||
store: store,
|
||||
ckContainerId: Constants.CloudKit.containerId,
|
||||
ckSharedContainerId: Constants.CloudKit.sharedContainerId,
|
||||
ckCoreDataZone: Constants.CloudKit.coreDataZone
|
||||
)
|
||||
|
||||
|
@ -94,6 +95,10 @@ final class AppContext {
|
|||
|
||||
private extension AppContext {
|
||||
func configureObjects() {
|
||||
coreContext.profileManager.willSaveSharedProfile = { [unowned self] in
|
||||
willSaveSharedProfile(withNewProfile: $0, existingProfile: $1)
|
||||
}
|
||||
|
||||
coreContext.vpnManager.isOnDemandRulesSupported = {
|
||||
self.isEligibleForOnDemandRules()
|
||||
}
|
||||
|
@ -101,6 +106,13 @@ private extension AppContext {
|
|||
self.isEligibleForNetworkSettings()
|
||||
}
|
||||
|
||||
coreContext.vpnManager.userData = {
|
||||
if let expirationDate = $0.connectionExpirationDate {
|
||||
return [Constants.Tunnel.expirationTimeIntervalKey: expirationDate.timeIntervalSinceReferenceDate]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
coreContext.vpnManager.currentState.$vpnStatus
|
||||
.removeDuplicates()
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
@ -138,4 +150,41 @@ private extension AppContext {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// eligibility: expire restricted TV profiles after N minutes
|
||||
func willSaveSharedProfile(withNewProfile newProfile: Profile, existingProfile: Profile?) -> Profile {
|
||||
if let existingProfile {
|
||||
assert(newProfile.id == existingProfile.id)
|
||||
}
|
||||
|
||||
guard productManager.isEligible(forFeature: .appleTV) else {
|
||||
var restricted = newProfile
|
||||
let remainingMinutes: Int
|
||||
let expirationDate: Date
|
||||
|
||||
// retain current expiration period if any
|
||||
if let currentExpirationDate = existingProfile?.connectionExpirationDate {
|
||||
remainingMinutes = Int(currentExpirationDate.timeIntervalSinceNow / 60.0)
|
||||
expirationDate = currentExpirationDate
|
||||
}
|
||||
// otherwise, expire in N minutes from now
|
||||
else {
|
||||
remainingMinutes = Constants.InApp.tvLimitedMinutes
|
||||
expirationDate = Date()
|
||||
.addingTimeInterval(TimeInterval(remainingMinutes) * 60.0)
|
||||
|
||||
restricted.connectionExpirationDate = expirationDate
|
||||
}
|
||||
|
||||
if remainingMinutes > 0 {
|
||||
pp_log.warning("\(newProfile.logDescription): TV connection expires in \(remainingMinutes) minutes (at \(expirationDate))")
|
||||
} else {
|
||||
pp_log.warning("\(newProfile.logDescription): TV connection expired at \(expirationDate)")
|
||||
}
|
||||
|
||||
return restricted
|
||||
}
|
||||
|
||||
return newProfile
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,14 @@ final class CoreContext {
|
|||
init(persistenceManager: PersistenceManager) {
|
||||
store = persistenceManager.store
|
||||
|
||||
#if !os(tvOS)
|
||||
let vpnPersistence = persistenceManager.loadVPNPersistence(
|
||||
withName: Constants.Persistence.profilesContainerName
|
||||
)
|
||||
#endif
|
||||
let sharedVPNPersistence = persistenceManager.loadSharedVPNPersistence(
|
||||
withName: Constants.Persistence.sharedProfilesContainerName
|
||||
)
|
||||
let providersPersistence = persistenceManager.loadProvidersPersistence(
|
||||
withName: Constants.Persistence.providersContainerName
|
||||
)
|
||||
|
@ -68,10 +73,20 @@ final class CoreContext {
|
|||
remoteProvidersStrategy: remoteProvidersStrategy
|
||||
)
|
||||
|
||||
let tvProfileRepository = sharedVPNPersistence.profileRepository()
|
||||
#if !os(tvOS)
|
||||
let profileRepository = vpnPersistence.profileRepository()
|
||||
let sharedProfileRepository = tvProfileRepository
|
||||
#else
|
||||
let profileRepository = tvProfileRepository
|
||||
let sharedProfileRepository: ProfileRepository? = nil
|
||||
#endif
|
||||
|
||||
profileManager = ProfileManager(
|
||||
store: store,
|
||||
providerManager: providerManager,
|
||||
profileRepository: vpnPersistence.profileRepository(),
|
||||
profileRepository: profileRepository,
|
||||
sharedProfileRepository: sharedProfileRepository,
|
||||
keychain: KeychainSecretRepository(appGroup: Constants.App.appGroupId),
|
||||
keychainEntry: Unlocalized.Keychain.passwordEntry,
|
||||
keychainLabel: Unlocalized.Keychain.passwordLabel
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// MockProfileRepository.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/18/23.
|
||||
// Copyright (c) 2023 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 Combine
|
||||
import Foundation
|
||||
import PassepartoutLibrary
|
||||
|
||||
final class MockProfileRepository: ProfileRepository, ObservableObject {
|
||||
@Published var profiles: [UUID: Profile]
|
||||
|
||||
init() {
|
||||
profiles = [:]
|
||||
}
|
||||
|
||||
func allProfiles() -> [UUID: Profile] {
|
||||
profiles
|
||||
}
|
||||
|
||||
func profile(withId id: UUID) -> Profile? {
|
||||
profiles[id]
|
||||
}
|
||||
|
||||
func saveProfiles(_ profiles: [Profile]) throws {
|
||||
profiles.forEach {
|
||||
self.profiles[$0.id] = $0
|
||||
}
|
||||
}
|
||||
|
||||
func removeProfiles(withIds ids: [UUID]) {
|
||||
ids.forEach {
|
||||
profiles.removeValue(forKey: $0)
|
||||
}
|
||||
}
|
||||
|
||||
func willUpdateProfiles() -> AnyPublisher<[UUID: Profile], Never> {
|
||||
$profiles.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
|
@ -33,6 +33,8 @@ enum AppError: Error {
|
|||
|
||||
case vpn(Passepartout.VPNError)
|
||||
|
||||
case tunnel(TunnelError)
|
||||
|
||||
case generic(Error)
|
||||
|
||||
init(_ error: Error) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Foundation
|
||||
import Intents
|
||||
import PassepartoutLibrary
|
||||
|
@ -157,3 +158,4 @@ extension IntentDispatcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Foundation
|
||||
import Intents
|
||||
import PassepartoutLibrary
|
||||
|
@ -157,3 +158,4 @@ private extension INInteraction {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(CFG_APP_ID)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
@ -80,7 +80,7 @@
|
|||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
|
@ -99,10 +99,12 @@
|
|||
<dict>
|
||||
<key>appstore_id</key>
|
||||
<string>$(CFG_APPSTORE_ID)</string>
|
||||
<key>group_id</key>
|
||||
<string>group.$(CFG_GROUP_ID)</string>
|
||||
<key>cloudkit_id</key>
|
||||
<string>iCloud.$(CFG_GROUP_ID)</string>
|
||||
<key>cloudkit_shared_id</key>
|
||||
<string>iCloud.$(CFG_GROUP_ID).Shared</string>
|
||||
<key>group_id</key>
|
||||
<string>group.$(CFG_GROUP_ID)</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -77,6 +77,12 @@ extension AppError: LocalizedError {
|
|||
return nil
|
||||
}
|
||||
|
||||
case .tunnel(let tunnelError):
|
||||
switch tunnelError {
|
||||
case .expired:
|
||||
return V.tunnelExpired
|
||||
}
|
||||
|
||||
case .generic(let error):
|
||||
return error.localizedDescription
|
||||
}
|
||||
|
|
|
@ -259,6 +259,8 @@ enum Unlocalized {
|
|||
|
||||
static let iCloud = "iCloud"
|
||||
|
||||
static let appleTV = "Apple TV"
|
||||
|
||||
static let totp = "TOTP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Combine
|
||||
import Foundation
|
||||
@preconcurrency import Intents
|
||||
|
@ -106,3 +107,4 @@ extension IntentsManager: INUIEditVoiceShortcutViewControllerDelegate {
|
|||
shouldDismissIntentView.send()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -35,10 +35,14 @@ final class PersistenceManager: ObservableObject {
|
|||
|
||||
private let ckContainerId: String
|
||||
|
||||
private let ckSharedContainerId: String
|
||||
|
||||
private let ckCoreDataZone: String
|
||||
|
||||
private var vpnPersistence: VPNPersistence?
|
||||
|
||||
private var sharedVPNPersistence: VPNPersistence?
|
||||
|
||||
private var providersPersistence: ProvidersPersistence?
|
||||
|
||||
private(set) var isCloudSyncingEnabled: Bool {
|
||||
|
@ -52,9 +56,13 @@ final class PersistenceManager: ObservableObject {
|
|||
|
||||
let didChangePersistence = PassthroughSubject<Void, Never>()
|
||||
|
||||
init(store: KeyValueStore, ckContainerId: String, ckCoreDataZone: String) {
|
||||
init(store: KeyValueStore,
|
||||
ckContainerId: String,
|
||||
ckSharedContainerId: String,
|
||||
ckCoreDataZone: String) {
|
||||
self.store = store
|
||||
self.ckContainerId = ckContainerId
|
||||
self.ckSharedContainerId = ckSharedContainerId
|
||||
self.ckCoreDataZone = ckCoreDataZone
|
||||
isCloudSyncingEnabled = store.canEnableCloudSyncing
|
||||
|
||||
|
@ -65,11 +73,23 @@ final class PersistenceManager: ObservableObject {
|
|||
}
|
||||
|
||||
func loadVPNPersistence(withName containerName: String) -> VPNPersistence {
|
||||
let persistence = VPNPersistence(withName: containerName, cloudKit: isCloudSyncingEnabled, author: persistenceAuthor)
|
||||
let persistence = VPNPersistence(withName: containerName,
|
||||
cloudKit: isCloudSyncingEnabled,
|
||||
cloudKitIdentifier: nil,
|
||||
author: persistenceAuthor)
|
||||
vpnPersistence = persistence
|
||||
return persistence
|
||||
}
|
||||
|
||||
func loadSharedVPNPersistence(withName containerName: String) -> VPNPersistence {
|
||||
let persistence = VPNPersistence(withName: containerName,
|
||||
cloudKit: true,
|
||||
cloudKitIdentifier: ckSharedContainerId,
|
||||
author: persistenceAuthor)
|
||||
sharedVPNPersistence = persistence
|
||||
return persistence
|
||||
}
|
||||
|
||||
func loadProvidersPersistence(withName containerName: String) -> ProvidersPersistence {
|
||||
let persistence = ProvidersPersistence(withName: containerName, cloudKit: false, author: persistenceAuthor)
|
||||
providersPersistence = persistence
|
||||
|
@ -86,6 +106,10 @@ extension PersistenceManager {
|
|||
fromContainerWithId: ckContainerId,
|
||||
zoneId: .init(zoneName: ckCoreDataZone)
|
||||
)
|
||||
await Self.eraseCloudKitStore(
|
||||
fromContainerWithId: ckSharedContainerId,
|
||||
zoneId: .init(zoneName: ckCoreDataZone)
|
||||
)
|
||||
isErasingCloudKitStore = false
|
||||
}
|
||||
|
||||
|
@ -109,7 +133,11 @@ private extension KeyValueStore {
|
|||
}
|
||||
|
||||
private var isCloudKitSupported: Bool {
|
||||
#if !os(tvOS)
|
||||
cloudKitToken != nil
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
}
|
||||
|
||||
var canEnableCloudSyncing: Bool {
|
||||
|
|
|
@ -33,6 +33,7 @@ struct PassepartoutApp: App {
|
|||
@SceneBuilder var body: some Scene {
|
||||
WindowGroup {
|
||||
MainView()
|
||||
#if !os(tvOS)
|
||||
.withoutTitleBar()
|
||||
.onIntentActivity(IntentDispatcher.connectVPN)
|
||||
.onIntentActivity(IntentDispatcher.disableVPN)
|
||||
|
@ -42,6 +43,7 @@ struct PassepartoutApp: App {
|
|||
.onIntentActivity(IntentDispatcher.trustCurrentNetwork)
|
||||
.onIntentActivity(IntentDispatcher.untrustCellularNetwork)
|
||||
.onIntentActivity(IntentDispatcher.untrustCurrentNetwork)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
|
@ -38,3 +39,4 @@ struct ActivityView: UIViewControllerRepresentable {
|
|||
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -180,7 +180,9 @@ private extension GenericCreditsView {
|
|||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
}.navigationTitle(content.name)
|
||||
#if !os(tvOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Intents
|
||||
import IntentsUI
|
||||
import SwiftUI
|
||||
|
@ -41,3 +42,4 @@ struct IntentAddView: UIViewControllerRepresentable {
|
|||
func updateUIViewController(_ uiViewController: INUIAddVoiceShortcutViewController, context: UIViewControllerRepresentableContext<IntentAddView>) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Intents
|
||||
import IntentsUI
|
||||
import SwiftUI
|
||||
|
@ -41,3 +42,4 @@ struct IntentEditView: UIViewControllerRepresentable {
|
|||
func updateUIViewController(_ uiViewController: INUIEditVoiceShortcutViewController, context: UIViewControllerRepresentableContext<IntentEditView>) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -29,9 +29,15 @@ struct LongContentView: View {
|
|||
@Binding var content: String
|
||||
|
||||
var body: some View {
|
||||
#if !os(tvOS)
|
||||
TextEditor(text: $content)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
// .padding()
|
||||
#else
|
||||
Text(content)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
// .padding()
|
||||
#endif
|
||||
|
||||
// TODO: layout, add padding an inset, let content extend beyond safe areas
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import MessageUI
|
||||
import SwiftUI
|
||||
|
||||
|
@ -82,3 +83,4 @@ extension MailComposerView {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -75,17 +75,21 @@ public final class Reviewer: ObservableObject {
|
|||
defaults.removeObject(forKey: Keys.eventCount)
|
||||
defaults.set(currentVersion, forKey: Keys.lastVersion)
|
||||
|
||||
#if !os(tvOS)
|
||||
requestReview()
|
||||
#endif
|
||||
return true
|
||||
}
|
||||
|
||||
// may or may not appear
|
||||
#if !os(tvOS)
|
||||
private func requestReview() {
|
||||
guard let scene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene else {
|
||||
return
|
||||
}
|
||||
SKStoreReviewController.requestReview(in: scene)
|
||||
}
|
||||
#endif
|
||||
|
||||
public static func urlForReview(withAppId appId: String) -> URL {
|
||||
URL(string: "https://apps.apple.com/app/id\(appId)?action=write-review")!
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Foundation
|
||||
import Intents
|
||||
|
||||
|
@ -53,3 +54,4 @@ struct Shortcut: Identifiable, Hashable, Comparable {
|
|||
native.invocationPhrase.lowercased()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -89,3 +90,4 @@ private extension ShortcutType {
|
|||
)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -33,10 +33,14 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
#if targetEnvironment(macCatalyst)
|
||||
MacBundle.shared.utils.sendAppToBackground()
|
||||
#endif
|
||||
#if !os(tvOS)
|
||||
rebuildShortcutItems()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
handleShortcutItem(shortcutItem)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ struct DebugLogView: View {
|
|||
refreshLog(scrollingToLatestWith: scrollProxy)
|
||||
}
|
||||
}
|
||||
#if !os(tvOS)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
.toolbar {
|
||||
Button(action: copyDebugLog) {
|
||||
|
@ -82,7 +83,9 @@ struct DebugLogView: View {
|
|||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
}.sheet(isPresented: $isSharing, content: sharingActivityView)
|
||||
}
|
||||
.sheet(isPresented: $isSharing, content: sharingActivityView)
|
||||
#endif
|
||||
#endif
|
||||
.edgesIgnoringSafeArea([.leading, .trailing])
|
||||
.onReceive(timer, perform: refreshLog)
|
||||
|
@ -104,9 +107,11 @@ private extension DebugLogView {
|
|||
// TODO: layout, a slight padding would be nice, but it glitches on first touch
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
func sharingActivityView() -> some View {
|
||||
ActivityView(activityItems: sharingItems)
|
||||
}
|
||||
#endif
|
||||
|
||||
var sharingItems: [Any] {
|
||||
let raw = logLines.joined(separator: "\n")
|
||||
|
@ -140,6 +145,7 @@ private extension DebugLogView {
|
|||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
func shareDebugLog() {
|
||||
guard !logLines.isEmpty else {
|
||||
assertionFailure("Log is empty, why could it share?")
|
||||
|
@ -159,6 +165,7 @@ private extension DebugLogView {
|
|||
|
||||
Utils.copyToPasteboard(content)
|
||||
}
|
||||
#endif
|
||||
|
||||
func scrollToLatestUpdate(_ proxy: ScrollViewProxy) {
|
||||
proxy.maybeScrollTo(logLines.count - 1, anchor: .bottomLeading)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
import TunnelKitOpenVPN
|
||||
|
@ -200,3 +201,4 @@ private extension DiagnosticsView.OpenVPNView {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -32,7 +32,9 @@ struct DiagnosticsView: View {
|
|||
var body: some View {
|
||||
Group {
|
||||
if !profile.isPlaceholder {
|
||||
#if !os(tvOS)
|
||||
vpnView
|
||||
#endif
|
||||
} else {
|
||||
genericView
|
||||
}
|
||||
|
@ -41,6 +43,8 @@ struct DiagnosticsView: View {
|
|||
}
|
||||
|
||||
private extension DiagnosticsView {
|
||||
|
||||
#if !os(tvOS)
|
||||
var vpnView: some View {
|
||||
Group {
|
||||
switch profile.currentVPNProtocol {
|
||||
|
@ -56,6 +60,7 @@ private extension DiagnosticsView {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
var genericView: some View {
|
||||
List {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
import TunnelKitCore
|
||||
|
@ -111,3 +112,4 @@ private extension EndpointView.AddView {
|
|||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
import TunnelKitOpenVPN
|
||||
|
@ -394,3 +395,4 @@ private extension Profile {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
import TunnelKitWireGuard
|
||||
|
@ -155,3 +156,4 @@ private extension ObservableProfile {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -48,3 +49,4 @@ struct EndpointView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
struct MainView: View {
|
||||
|
@ -33,3 +34,4 @@ struct MainView: View {
|
|||
}.themeGlobal()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -137,8 +137,10 @@ private extension OnDemandView {
|
|||
guard isEligibleForSiri else {
|
||||
return
|
||||
}
|
||||
#if !os(tvOS)
|
||||
IntentDispatcher.donateTrustCellularNetwork()
|
||||
IntentDispatcher.donateUntrustCellularNetwork()
|
||||
#endif
|
||||
}
|
||||
|
||||
// eligibility: donate intents if eligible for Siri
|
||||
|
@ -146,7 +148,9 @@ private extension OnDemandView {
|
|||
guard isEligibleForSiri else {
|
||||
return
|
||||
}
|
||||
#if !os(tvOS)
|
||||
IntentDispatcher.donateTrustCurrentNetwork()
|
||||
IntentDispatcher.donateUntrustCurrentNetwork()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -56,6 +57,7 @@ extension OrganizerView {
|
|||
VPNToggle(
|
||||
profile: profile,
|
||||
interactiveProfile: interactiveProfile,
|
||||
title: L10n.Global.Strings.enabled,
|
||||
rateLimit: Constants.RateLimit.vpnToggle
|
||||
).labelsHidden()
|
||||
}.padding([.top, .bottom], 10)
|
||||
|
@ -81,3 +83,4 @@ private extension OrganizerView.ProfileRow {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -187,3 +188,4 @@ private extension OrganizerView.ProfilesList {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -84,3 +85,4 @@ private extension OrganizerView.SceneView {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
|
@ -89,12 +90,14 @@ struct OrganizerView: View {
|
|||
presenting: alertType,
|
||||
actions: alertActions,
|
||||
message: alertMessage
|
||||
).fileImporter(
|
||||
)
|
||||
.fileImporter(
|
||||
isPresented: $isHostFileImporterPresented,
|
||||
allowedContentTypes: hostFileTypes,
|
||||
allowsMultipleSelection: false,
|
||||
onCompletion: onHostFileImporterResult
|
||||
).onOpenURL(perform: onOpenURL)
|
||||
)
|
||||
.onOpenURL(perform: onOpenURL)
|
||||
.themePrimaryView()
|
||||
}
|
||||
}
|
||||
|
@ -171,3 +174,4 @@ private extension OrganizerView {
|
|||
addProfileModalType = .addHost(url, false)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -44,6 +44,8 @@ extension PaywallView {
|
|||
|
||||
@State private var purchaseState: PurchaseState?
|
||||
|
||||
@State private var didPurchaseAppleTV = false
|
||||
|
||||
init(isPresented: Binding<Bool>, feature: LocalProduct? = nil) {
|
||||
productManager = .shared
|
||||
_isPresented = isPresented
|
||||
|
@ -52,12 +54,22 @@ extension PaywallView {
|
|||
|
||||
var body: some View {
|
||||
List {
|
||||
featuresSection
|
||||
skFullVersion.map {
|
||||
fullFeaturesSection(withHeader: $0.localizedTitle)
|
||||
}
|
||||
purchaseSection
|
||||
.disabled(purchaseState != nil)
|
||||
restoreSection
|
||||
.disabled(purchaseState != nil)
|
||||
}.navigationTitle(Unlocalized.appName)
|
||||
}
|
||||
.navigationTitle(Unlocalized.appName)
|
||||
.alert(Unlocalized.Other.appleTV, isPresented: $didPurchaseAppleTV) {
|
||||
Button(L10n.Global.Strings.ok) {
|
||||
isPresented = false
|
||||
}
|
||||
} message: {
|
||||
Text(L10n.Paywall.Alerts.Purchase.Appletv.Success.message)
|
||||
}
|
||||
|
||||
// reloading
|
||||
.task {
|
||||
|
@ -121,9 +133,9 @@ private struct PurchaseRow: View {
|
|||
// MARK: -
|
||||
|
||||
private extension PaywallView.PurchaseView {
|
||||
var featuresSection: some View {
|
||||
func fullFeaturesSection(withHeader header: String) -> some View {
|
||||
Section {
|
||||
ForEach(features) { feature in
|
||||
ForEach(fullFeatures) { feature in
|
||||
VStack(alignment: .leading) {
|
||||
Text(feature.title)
|
||||
.themeCellTitleStyle()
|
||||
|
@ -134,6 +146,8 @@ private extension PaywallView.PurchaseView {
|
|||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(header)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +197,9 @@ private extension PaywallView.PurchaseView {
|
|||
|
||||
// hide full version if already bought the other platform version
|
||||
var skFullVersion: InAppProduct? {
|
||||
guard !productManager.isFullVersion() else {
|
||||
return nil
|
||||
}
|
||||
#if targetEnvironment(macCatalyst)
|
||||
guard !productManager.hasPurchased(.fullVersion_iOS) else {
|
||||
return nil
|
||||
|
@ -195,17 +212,25 @@ private extension PaywallView.PurchaseView {
|
|||
return productManager.product(withIdentifier: .fullVersion)
|
||||
}
|
||||
|
||||
var features: [FeatureModel] {
|
||||
var skAppleTV: InAppProduct? {
|
||||
guard feature == .appleTV else {
|
||||
return nil
|
||||
}
|
||||
return productManager.product(withIdentifier: .appleTV)
|
||||
}
|
||||
|
||||
var fullFeatures: [FeatureModel] {
|
||||
productManager.featureProducts(excluding: {
|
||||
$0 == .fullVersion || $0.isPlatformVersion
|
||||
$0 == .fullVersion || $0 == .appleTV || $0.isLegacyPlatformVersion
|
||||
})
|
||||
.map {
|
||||
FeatureModel(product: $0)
|
||||
}
|
||||
.sorted()
|
||||
}
|
||||
|
||||
var productRowModels: [InAppProduct] {
|
||||
[skFullVersion]
|
||||
[skFullVersion, skAppleTV]
|
||||
.compactMap { $0 }
|
||||
}
|
||||
}
|
||||
|
@ -249,16 +274,25 @@ private extension PurchaseRow {
|
|||
|
||||
// MARK: -
|
||||
|
||||
// IMPORTANT: resync shared profiles after purchasing Apple TV feature (drop time limit)
|
||||
|
||||
private extension PaywallView.PurchaseView {
|
||||
func purchaseProduct(_ product: InAppProduct) {
|
||||
purchaseState = .purchasing(product)
|
||||
|
||||
Task {
|
||||
do {
|
||||
let wasEligibleForAppleTV = productManager.isEligible(forFeature: .appleTV)
|
||||
let result = try await productManager.purchase(product)
|
||||
|
||||
switch result {
|
||||
case .done:
|
||||
if !wasEligibleForAppleTV && productManager.isEligible(forFeature: .appleTV) {
|
||||
ProfileManager.shared.refreshSharedProfiles()
|
||||
didPurchaseAppleTV = true
|
||||
} else {
|
||||
isPresented = false
|
||||
}
|
||||
|
||||
case .cancelled:
|
||||
break
|
||||
|
@ -281,8 +315,16 @@ private extension PaywallView.PurchaseView {
|
|||
|
||||
Task {
|
||||
do {
|
||||
let wasEligibleForAppleTV = productManager.isEligible(forFeature: .appleTV)
|
||||
try await productManager.restorePurchases()
|
||||
|
||||
if !wasEligibleForAppleTV && productManager.isEligible(forFeature: .appleTV) {
|
||||
ProfileManager.shared.refreshSharedProfiles()
|
||||
didPurchaseAppleTV = true
|
||||
} else {
|
||||
isPresented = false
|
||||
}
|
||||
|
||||
purchaseState = nil
|
||||
} catch {
|
||||
pp_log.error("Unable to restore purchases: \(error)")
|
||||
|
|
|
@ -53,11 +53,13 @@ extension ProfileView {
|
|||
Label(L10n.Global.Strings.protocol, systemImage: themeVPNProtocolImage)
|
||||
.withTrailingText(currentProfile.value.currentVPNProtocol.description)
|
||||
}
|
||||
#if !os(tvOS)
|
||||
NavigationLink {
|
||||
EndpointView(currentProfile: currentProfile)
|
||||
} label: {
|
||||
Label(L10n.Global.Strings.endpoint, systemImage: themeEndpointImage)
|
||||
}
|
||||
#endif
|
||||
if currentProfile.value.requiresCredentials {
|
||||
NavigationLink {
|
||||
AccountView(
|
||||
|
|
|
@ -96,6 +96,7 @@ extension ProfileView {
|
|||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
struct ShortcutsButton: View {
|
||||
@ObservedObject private var productManager: ProductManager
|
||||
|
||||
|
@ -114,6 +115,7 @@ extension ProfileView {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct RenameButton: View {
|
||||
@Binding private var modalType: ModalType?
|
||||
|
@ -164,9 +166,11 @@ private extension ProfileView.MainMenu {
|
|||
var mainView: some View {
|
||||
Menu {
|
||||
ProfileView.ReconnectButton()
|
||||
#if !os(tvOS)
|
||||
ProfileView.ShortcutsButton(
|
||||
modalType: $modalType
|
||||
)
|
||||
#endif
|
||||
Divider()
|
||||
ProfileView.RenameButton(
|
||||
modalType: $modalType
|
||||
|
@ -238,11 +242,13 @@ private extension ProfileView.MainMenu {
|
|||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
private extension ProfileView.ShortcutsButton {
|
||||
var isEligibleForSiri: Bool {
|
||||
productManager.isEligible(forFeature: .siriShortcuts)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: -
|
||||
|
||||
|
@ -260,6 +266,7 @@ private extension ProfileView.MainMenu {
|
|||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
private extension ProfileView.ShortcutsButton {
|
||||
func presentShortcutsOrPaywall() {
|
||||
|
||||
|
@ -271,6 +278,7 @@ private extension ProfileView.ShortcutsButton {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private extension ProfileView.DuplicateButton {
|
||||
func duplicateProfile(withId id: UUID) {
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// ProfileView+TV.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/22/23.
|
||||
// Copyright (c) 2023 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 PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
extension ProfileView {
|
||||
struct TVSection: View {
|
||||
@ObservedObject private var profileManager: ProfileManager
|
||||
|
||||
@ObservedObject private var productManager: ProductManager
|
||||
|
||||
@ObservedObject private var currentProfile: ObservableProfile
|
||||
|
||||
@Binding private var isProfileShared: Bool
|
||||
|
||||
@Binding private var modalType: ModalType?
|
||||
|
||||
init(currentProfile: ObservableProfile, modalType: Binding<ModalType?>) {
|
||||
let profileManager: ProfileManager = .shared
|
||||
|
||||
self.profileManager = profileManager
|
||||
productManager = .shared
|
||||
self.currentProfile = currentProfile
|
||||
_isProfileShared = Binding {
|
||||
profileManager.isSharing(profile: currentProfile.value)
|
||||
} set: {
|
||||
profileManager.setSharing($0, profile: currentProfile.value)
|
||||
}
|
||||
_modalType = modalType
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
Toggle(isOn: $isProfileShared) {
|
||||
Label(shareText, systemImage: themeAppleTVImage)
|
||||
}
|
||||
|
||||
// eligibility: present paywall for full support for Apple TV
|
||||
if !isEligibleForAppleTV {
|
||||
Button(L10n.Paywall.title) {
|
||||
modalType = .paywallAppleTV
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
Text(footerText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ProfileView.TVSection {
|
||||
var isEligibleForAppleTV: Bool {
|
||||
productManager.isEligible(forFeature: .appleTV)
|
||||
}
|
||||
|
||||
var shareText: String {
|
||||
var sentences: [String] = [Unlocalized.Other.appleTV]
|
||||
if !isEligibleForAppleTV {
|
||||
sentences.append(L10n.Profile.Items.TvSharing.Caption.limited(Constants.InApp.tvLimitedMinutes))
|
||||
}
|
||||
return sentences.joined(separator: " — ")
|
||||
}
|
||||
|
||||
var footerText: String {
|
||||
var sentences: [String] = [L10n.Profile.Sections.Tv.Footer.encryption]
|
||||
if !isEligibleForAppleTV {
|
||||
sentences.append(L10n.Profile.Sections.Tv.Footer.Restricted.p1(Constants.InApp.tvLimitedMinutes))
|
||||
sentences.append(L10n.Profile.Sections.Tv.Footer.Restricted.p2)
|
||||
}
|
||||
return sentences.joined(separator: " ")
|
||||
}
|
||||
}
|
|
@ -73,6 +73,7 @@ private extension ProfileView.VPNSection {
|
|||
VPNToggle(
|
||||
profile: profile,
|
||||
interactiveProfile: interactiveProfile,
|
||||
title: L10n.Global.Strings.enabled,
|
||||
rateLimit: Constants.RateLimit.vpnToggle
|
||||
)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ struct ProfileView: View {
|
|||
enum ModalType: Int, Identifiable {
|
||||
case interactiveAccount
|
||||
|
||||
#if !os(tvOS)
|
||||
case shortcuts
|
||||
#endif
|
||||
|
||||
case rename
|
||||
|
||||
|
@ -40,6 +42,8 @@ struct ProfileView: View {
|
|||
|
||||
case paywallTrustedNetworks
|
||||
|
||||
case paywallAppleTV
|
||||
|
||||
var id: Int {
|
||||
rawValue
|
||||
}
|
||||
|
@ -104,6 +108,10 @@ private extension ProfileView {
|
|||
currentProfile: currentProfile,
|
||||
modalType: $modalType
|
||||
)
|
||||
TVSection(
|
||||
currentProfile: currentProfile,
|
||||
modalType: $modalType
|
||||
)
|
||||
ExtraSection(currentProfile: currentProfile)
|
||||
Section {
|
||||
DiagnosticsRow(currentProfile: currentProfile)
|
||||
|
@ -122,10 +130,12 @@ private extension ProfileView {
|
|||
InteractiveConnectionView(profile: currentProfile.value)
|
||||
}.themeGlobal()
|
||||
|
||||
#if !os(tvOS)
|
||||
case .shortcuts:
|
||||
NavigationView {
|
||||
ShortcutsView(target: currentProfile.value)
|
||||
}.themeGlobal()
|
||||
#endif
|
||||
|
||||
case .rename:
|
||||
NavigationView {
|
||||
|
@ -155,6 +165,14 @@ private extension ProfileView {
|
|||
feature: .trustedNetworks
|
||||
)
|
||||
}.themeGlobal()
|
||||
|
||||
case .paywallAppleTV:
|
||||
NavigationView {
|
||||
PaywallView(
|
||||
modalType: $modalType,
|
||||
feature: .appleTV
|
||||
)
|
||||
}.themeGlobal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,9 +168,11 @@ private extension ProviderLocationView {
|
|||
ForEach(filteredLocations(for: category)) { location in
|
||||
if isEditable {
|
||||
locationRow(location)
|
||||
#if !os(tvOS)
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
favoriteActions(location)
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
locationRow(location)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import MessageUI
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
@ -88,3 +89,4 @@ struct ReportIssueView: View {
|
|||
)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Intents
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
@ -162,3 +163,4 @@ private extension ShortcutsView.AddView {
|
|||
pendingShortcut = shortcut
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#if !os(tvOS)
|
||||
import Intents
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
@ -176,3 +177,4 @@ private extension ShortcutsView {
|
|||
modalType = .add(shortcut: shortcut)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
//
|
||||
// ActiveProfileView+TV.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/18/23.
|
||||
// Copyright (c) 2023 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/>.
|
||||
//
|
||||
|
||||
#if os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
struct ActiveProfileView: View {
|
||||
@ObservedObject private var profileManager: ProfileManager
|
||||
|
||||
@ObservedObject private var vpnState: ObservableVPNState
|
||||
|
||||
init(profileManager: ProfileManager) {
|
||||
self.profileManager = profileManager
|
||||
vpnState = .shared
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
let activeProfile = profileManager.activeProfile
|
||||
nameView(for: activeProfile)
|
||||
if let activeProfile {
|
||||
toggleView(for: activeProfile)
|
||||
vpnProtocolView(for: activeProfile)
|
||||
statusView(for: activeProfile)
|
||||
if let expirationDate = activeProfile.connectionExpirationDate {
|
||||
expirationView(at: expirationDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let activeProfile = profileManager.activeProfile,
|
||||
let server = activeProfile.providerServer(.shared) {
|
||||
providerSection(with: server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ActiveProfileView {
|
||||
func nameView(for profile: Profile?) -> some View {
|
||||
NavigationLink {
|
||||
ProfilesListView(profileManager: profileManager)
|
||||
} label: {
|
||||
Text(L10n.Global.Placeholders.profileName)
|
||||
.withTrailingText(profile?.header.name)
|
||||
}
|
||||
}
|
||||
|
||||
func vpnProtocolView(for profile: Profile) -> some View {
|
||||
Text(L10n.Global.Strings.protocol)
|
||||
.withTrailingText(profile.currentVPNProtocol.description)
|
||||
}
|
||||
|
||||
func toggleView(for profile: Profile) -> some View {
|
||||
VPNToggle(
|
||||
profile: profile,
|
||||
interactiveProfile: .constant(nil),
|
||||
title: L10n.Profile.Items.ConnectionStatus.caption,
|
||||
rateLimit: Constants.RateLimit.vpnToggle
|
||||
)
|
||||
}
|
||||
|
||||
func statusView(for activeProfile: Profile) -> some View {
|
||||
HStack {
|
||||
Text(Unlocalized.VPN.vpn)
|
||||
Spacer()
|
||||
if vpnState.isEnabled && activeProfile.isExpired {
|
||||
Text(L10n.Global.Errors.tunnelExpired)
|
||||
.themeSecondaryTextStyle()
|
||||
} else {
|
||||
VPNStatusText(isActiveProfile: true)
|
||||
.themeSecondaryTextStyle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expirationView(at expirationDate: Date) -> some View {
|
||||
Text(L10n.Profile.Items.ExpiresAt.caption)
|
||||
.withTrailingText(expirationDate.timestamp)
|
||||
}
|
||||
|
||||
func providerSection(with server: ProviderServer) -> some View {
|
||||
Section {
|
||||
Text(L10n.Global.Strings.name)
|
||||
.withTrailingText(server.providerMetadata.fullName)
|
||||
HStack {
|
||||
Text(L10n.Provider.Location.title)
|
||||
Spacer()
|
||||
Label(server.localizedDescription(style: .country), image: themeAssetsCountryImage(server.countryCode))
|
||||
.themeSecondaryTextStyle()
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.provider)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// MainView+TV.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/17/23.
|
||||
// Copyright (c) 2023 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/>.
|
||||
//
|
||||
|
||||
#if os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
struct MainView: View {
|
||||
@State private var path = NavigationPath()
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $path) {
|
||||
OrganizerView()
|
||||
}
|
||||
.themeGlobal()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainView()
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// OrganizerView+TV.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/17/23.
|
||||
// Copyright (c) 2023 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/>.
|
||||
//
|
||||
|
||||
#if os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
struct OrganizerView: View {
|
||||
@ObservedObject private var profileManager: ProfileManager
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
@State private var didLoadMockProfiles = false
|
||||
#endif
|
||||
|
||||
init(profileManager: ProfileManager = .shared) {
|
||||
self.profileManager = profileManager
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ActiveProfileView(profileManager: profileManager)
|
||||
aboutSection
|
||||
}
|
||||
.navigationTitle(Unlocalized.appName)
|
||||
.themeTV()
|
||||
.themeAnimation(on: profileManager.activeProfileId)
|
||||
#if targetEnvironment(simulator)
|
||||
.task {
|
||||
await loadMockProfiles()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private extension OrganizerView {
|
||||
var aboutSection: some View {
|
||||
Section {
|
||||
Text(L10n.Version.title)
|
||||
.withTrailingText(Constants.Global.appVersionString)
|
||||
} header: {
|
||||
Text(L10n.About.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Mock
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
import TunnelKitOpenVPN
|
||||
import TunnelKitWireGuard
|
||||
|
||||
// poor man's preview:
|
||||
//
|
||||
// https://developer.apple.com/forums/thread/719078
|
||||
|
||||
private let mockHosts: [(String, VPNProtocolType)] = [
|
||||
("My Profile", .wireGuard),
|
||||
("Friend's House", .openVPN),
|
||||
("At School", .wireGuard)
|
||||
]
|
||||
|
||||
private let mockProviders: [ProviderName] = [
|
||||
.hideme,
|
||||
.pia,
|
||||
.protonvpn
|
||||
]
|
||||
|
||||
@MainActor
|
||||
private let mockRepository: ProfileRepository = {
|
||||
let hostProfiles = mockHosts.map { name, vpnType in
|
||||
let header = Profile.Header(name: name)
|
||||
switch vpnType {
|
||||
case .openVPN:
|
||||
let ovpn = OpenVPN.ConfigurationBuilder()
|
||||
return Profile(header, configuration: ovpn.build())
|
||||
|
||||
case .wireGuard:
|
||||
let wg = WireGuard.ConfigurationBuilder()
|
||||
return Profile(header, configuration: wg.build())
|
||||
}
|
||||
}
|
||||
|
||||
let providerProfiles = mockProviders.map { providerName in
|
||||
let manager = ProviderManager.shared
|
||||
let metadata = manager.provider(withName: providerName)!
|
||||
let header = Profile.Header(name: metadata.fullName, providerName: providerName)
|
||||
var provider = Profile.Provider(providerName)
|
||||
let vpnType: VPNProtocolType = .openVPN // isOpenVPN ? .openVPN : .wireGuard
|
||||
var settings = Profile.Provider.Settings()
|
||||
let anyServer = manager.anyDefaultServer(providerName, vpnProtocol: vpnType)
|
||||
settings.serverId = anyServer?.id
|
||||
settings.presetId = anyServer?.presetIds.first
|
||||
settings.account = .init("hello", "world")
|
||||
provider.vpnSettings[vpnType] = settings
|
||||
return Profile(header, provider: provider)
|
||||
}
|
||||
|
||||
var profiles: [Profile] = []
|
||||
profiles.append(contentsOf: hostProfiles)
|
||||
profiles.append(contentsOf: providerProfiles)
|
||||
|
||||
let repo = MockProfileRepository()
|
||||
try? repo.saveProfiles(profiles.map {
|
||||
var copy = $0
|
||||
copy.connectionExpirationDate = Date().addingTimeInterval(10.0)
|
||||
return copy
|
||||
})
|
||||
return repo
|
||||
}()
|
||||
|
||||
private extension OrganizerView {
|
||||
func loadMockProfiles() async {
|
||||
guard !didLoadMockProfiles else {
|
||||
return
|
||||
}
|
||||
do {
|
||||
let providerManager: ProviderManager = .shared
|
||||
try await providerManager.fetchProvidersIndexPublisher(priority: .bundle).async()
|
||||
|
||||
for name in mockProviders {
|
||||
try? await providerManager.fetchProviderPublisher(withName: name, vpnProtocol: .openVPN, priority: .bundle).async()
|
||||
}
|
||||
|
||||
profileManager.swapProfileRepository(mockRepository)
|
||||
profileManager.activateProfile(mockRepository.allProfiles().first!.value)
|
||||
|
||||
didLoadMockProfiles = true
|
||||
} catch {
|
||||
ErrorHandler.shared.handle(AppError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
OrganizerView()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// ProfilesList+TV.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/18/23.
|
||||
// Copyright (c) 2023 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/>.
|
||||
//
|
||||
|
||||
#if os(tvOS)
|
||||
import PassepartoutLibrary
|
||||
import SwiftUI
|
||||
|
||||
struct ProfilesListView: View {
|
||||
@ObservedObject private var profileManager: ProfileManager
|
||||
|
||||
@Environment(\.presentationMode) private var presentationMode
|
||||
|
||||
@State private var profileIdPendingSelection: UUID?
|
||||
|
||||
@FocusState private var focusedProfileId: UUID?
|
||||
|
||||
init(profileManager: ProfileManager) {
|
||||
self.profileManager = profileManager
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
Text(listHeader)
|
||||
.font(.footnote)
|
||||
.themeSecondaryTextStyle()
|
||||
ForEach(profiles, content: profileRow)
|
||||
.themeAnimation(on: profiles)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
focusedProfileId = profileManager.activeProfileId
|
||||
}
|
||||
.disabled(profileIdPendingSelection != nil)
|
||||
.navigationTitle(Text(L10n.Global.Strings.profiles))
|
||||
.themeTV()
|
||||
}
|
||||
}
|
||||
|
||||
private extension ProfilesListView {
|
||||
var profiles: [Profile] {
|
||||
profileManager.profiles.sorted()
|
||||
}
|
||||
|
||||
var listHeader: String {
|
||||
[
|
||||
L10n.Organizer.Sections.Tv.ProfilesList.Header.p1,
|
||||
L10n.Profile.Sections.Tv.Footer.encryption
|
||||
]
|
||||
.joined(separator: " ")
|
||||
}
|
||||
|
||||
func profileRow(for profile: Profile) -> some View {
|
||||
Button {
|
||||
activateProfile(profile)
|
||||
} label: {
|
||||
HStack {
|
||||
Text(profile.header.name)
|
||||
Spacer()
|
||||
if profile.header.id == profileIdPendingSelection {
|
||||
ProgressView()
|
||||
} else if profileManager.isActiveProfile(profile.header.id) {
|
||||
themeCheckmarkImage.asSystemImage
|
||||
}
|
||||
}
|
||||
}
|
||||
.focused($focusedProfileId, equals: profile.id)
|
||||
}
|
||||
|
||||
func activateProfile(_ profile: Profile) {
|
||||
guard profile.id != profileManager.activeProfileId else {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
return
|
||||
}
|
||||
Task {
|
||||
profileIdPendingSelection = profile.id
|
||||
do {
|
||||
try await profileManager.makeProfileReady(profile)
|
||||
await VPNManager.shared.disable()
|
||||
profileManager.activateProfile(profile)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
} catch {
|
||||
ErrorHandler.shared.handle(
|
||||
title: L10n.Global.Strings.profiles,
|
||||
message: AppError(error).localizedDescription
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -39,22 +39,25 @@ struct VPNToggle: View {
|
|||
|
||||
@Binding private var interactiveProfile: Profile?
|
||||
|
||||
private let title: String
|
||||
|
||||
private let rateLimit: Int
|
||||
|
||||
@State private var canToggle = true
|
||||
|
||||
init(profile: Profile, interactiveProfile: Binding<Profile?>, rateLimit: Int) {
|
||||
init(profile: Profile, interactiveProfile: Binding<Profile?>, title: String, rateLimit: Int) {
|
||||
profileManager = .shared
|
||||
vpnManager = .shared
|
||||
currentVPNState = .shared
|
||||
productManager = .shared
|
||||
self.profile = profile
|
||||
_interactiveProfile = interactiveProfile
|
||||
self.title = title
|
||||
self.rateLimit = rateLimit
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Toggle(L10n.Global.Strings.enabled, isOn: isEnabled)
|
||||
Toggle(title, isOn: isEnabled)
|
||||
.disabled(!canToggle)
|
||||
.themeAnimation(on: currentVPNState.isEnabled)
|
||||
}
|
||||
|
@ -129,11 +132,13 @@ private extension VPNToggle {
|
|||
|
||||
pp_log.debug("Donating connection intents...")
|
||||
|
||||
#if !os(tvOS)
|
||||
IntentDispatcher.donateEnableVPN()
|
||||
IntentDispatcher.donateDisableVPN()
|
||||
IntentDispatcher.donateConnection(
|
||||
with: profile,
|
||||
providerManager: ProviderManager.shared
|
||||
)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,9 +71,11 @@ extension View {
|
|||
.truncationMode(truncationMode)
|
||||
if copyOnTap {
|
||||
trailing
|
||||
#if !os(tvOS)
|
||||
.onTapGesture {
|
||||
text.map(Utils.copyToPasteboard)
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
trailing
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
"global.strings.authentication" = "Authentifizierung";
|
||||
"global.messages.unlock_app" = "Passepartout ist gesperrt";
|
||||
"global.messages.email_not_configured" = "Es wurde kein Email-Account konfiguriert.";
|
||||
"global.messages.share" = "Passepartout ist ein Benutzerfreundlicher, Open Source OpenVPN / WireGuard client für iOS und macOS";
|
||||
"global.messages.share" = "Passepartout ist ein Benutzerfreundlicher, Open Source OpenVPN / WireGuard client für iOS und macOS"; // FIXME: l10n, Apple platforms
|
||||
|
||||
"global.alerts.buttons.remind" = "Später erinnern";
|
||||
"global.alerts.buttons.never" = "Nicht erneut fragen";
|
||||
|
@ -83,6 +83,7 @@
|
|||
"global.errors.missing_account" = "Fehlender Account";
|
||||
"global.errors.missing_provider_server" = "Fehlender Standort";
|
||||
"global.errors.missing_provider_preset" = "Fehlende Voreinstellung";
|
||||
"global.errors.tunnel_expired" = "Verbindung abgelaufen";
|
||||
|
||||
/* MARK: Menus */
|
||||
|
||||
|
@ -132,6 +133,7 @@
|
|||
/* MARK: OrganizerView */
|
||||
|
||||
"organizer.sections.active" = "In Benutzung";
|
||||
"organizer.sections.tv.profiles_list.header.p1" = "Öffne Passepartout auf deinem iOS- oder macOS-Gerät und aktiviere den „Apple TV“-Schalter eines Profils, damit es hier angezeigt wird.";
|
||||
/* MARK: OrganizerView */
|
||||
"organizer.empty.no_profiles" = "Keine Profile";
|
||||
|
||||
|
@ -163,6 +165,9 @@
|
|||
"profile.sections.vpn.footer" = "Die Verbindung wird immer aufgebaut wenn notwendig.";
|
||||
"profile.sections.status.header" = "Verbindung";
|
||||
"profile.sections.provider_infrastructure.footer" = "Zuletzt aktualisiert am %@.";
|
||||
"profile.sections.tv.footer.encryption" = "Profile sind verschlüsselt und werden deinem Apple TV über iCloud zur Verfügung gestellt.";
|
||||
"profile.sections.tv.footer.restricted.p1" = "Die Verbindung läuft jedoch nach %d Minuten ab.";
|
||||
"profile.sections.tv.footer.restricted.p2" = "Bei einem Kauf entfällt diese Beschränkung.";
|
||||
"profile.sections.vpn_survives_sleep.footer" = "Deaktivieren um die Batterielaufzeit zu verbessern, allerdings verzögert sich der Verbindungsaufbau beim Aufwachen.";
|
||||
"profile.sections.vpn_resolves_hostname.footer" = "Bevorzugt in den meisten Netzwerken und benötigt in manchen IPv6 Netzwerken. Deaktivieren wo DNS geblockt ist oder um die Aushandlung zu beschleunigen bei langsam antwortenden DNS.";
|
||||
"profile.sections.feedback.header" = "Feedback";
|
||||
|
@ -178,6 +183,8 @@
|
|||
"profile.items.only_shows_favorites.caption" = "Nur favorisierte Standorte anzeigen";
|
||||
"profile.items.vpn_survives_sleep.caption" = "Verbindung aktiv halten trotz Schlafmodus";
|
||||
"profile.items.vpn_resolves_hostname.caption" = "Server Hostname auflösen";
|
||||
"profile.items.tv_sharing.caption.limited" = "Begrenzt auf %d Minuten";
|
||||
"profile.items.expires_at.caption" = "Endet";
|
||||
|
||||
"profile.alerts.rename.title" = "Profil umbenennen";
|
||||
"profile.alerts.reconnect_vpn.message" = "Möchtest du erneut zum VPN verbinden?";
|
||||
|
@ -323,6 +330,7 @@
|
|||
"paywall.items.full_version.extra_description" = "Alle Anbieter (inklusive Zukünftige)\n%@";
|
||||
"paywall.items.restore.title" = "Einkäufe wiederherstellen";
|
||||
"paywall.items.restore.description" = "Wenn Sie diese App oder Funktion in der Vergangenheit gekauft haben, können Sie Ihre Einkäufe wiederherstellen und dieser Bildschirm wird nicht mehr angezeigt.";
|
||||
"paywall.alerts.purchase.appletv.success.message" = "Vielen Dank! Das Zeitlimit entfällt, sobald iCloud auf dem neuesten Stand ist. Warte einen Moment und starte die Verbindung in der TV-App dann neu.";
|
||||
|
||||
/* MARK: DonateView */
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
"global.strings.authentication" = "Αυθεντικοποίηση";
|
||||
"global.messages.unlock_app" = "Το πασπαρτού είναι κλειδωμένο";
|
||||
"global.messages.email_not_configured" = "Δεν έχει ρυθμιστεί λογαριασμός ηλεκτρονικού ταχυδρομείου.";
|
||||
"global.messages.share" = "Το Passepartout είναι φιλικό προς το χρήστη, ανοιχτού κώδικα OpenVPN / WireGuard πρόγραμμα για iOS και macOS";
|
||||
"global.messages.share" = "Το Passepartout είναι φιλικό προς το χρήστη, ανοιχτού κώδικα OpenVPN / WireGuard πρόγραμμα για iOS και macOS"; // FIXME: l10n, Apple platforms
|
||||
|
||||
"global.alerts.buttons.remind" = "Υπενθύμιση Αργότερα";
|
||||
"global.alerts.buttons.never" = "Μη με ρωτήσεις ξανά";
|
||||
|
@ -83,6 +83,7 @@
|
|||
"global.errors.missing_account" = "Λείπει λογαριασμός";
|
||||
"global.errors.missing_provider_server" = "Λείπει η τοποθεσία";
|
||||
"global.errors.missing_provider_preset" = "Λείπει προεπιλογή";
|
||||
"global.errors.tunnel_expired" = "H σύνδεση έληξε";
|
||||
|
||||
/* MARK: Menus */
|
||||
|
||||
|
@ -132,6 +133,7 @@
|
|||
/* MARK: OrganizerView */
|
||||
|
||||
"organizer.sections.active" = "Σε χρήση";
|
||||
"organizer.sections.tv.profiles_list.header.p1" = "Ανοίξτε το Passepartout στη συσκευή σας iOS ή macOS και ενεργοποιήστε την εναλλαγή \"Apple TV\" ενός προφίλ για να εμφανιστεί εδώ.";
|
||||
/* MARK: OrganizerView */
|
||||
"organizer.empty.no_profiles" = "Δεν υπάρχουν προφίλ";
|
||||
|
||||
|
@ -163,6 +165,9 @@
|
|||
"profile.sections.vpn.footer" = "Η σύνδεση θα πραγματοποιηθεί όποτε είναι απαραίτητο.";
|
||||
"profile.sections.status.header" = "Σύνδεση";
|
||||
"profile.sections.provider_infrastructure.footer" = "Τελευταία ενημέρωση στις %@.";
|
||||
"profile.sections.tv.footer.encryption" = "Τα προφίλ κρυπτογραφούνται και διατίθενται στο Apple TV σας μέσω iCloud.";
|
||||
"profile.sections.tv.footer.restricted.p1" = "Ωστόσο, η σύνδεση θα λήξει μετά από %d λεπτά.";
|
||||
"profile.sections.tv.footer.restricted.p2" = "Αγορά για την κατάργηση του περιορισμού.";
|
||||
"profile.sections.vpn_survives_sleep.footer" = "Απενεργοποιήστε για να βελτιώσετε τη χρήση της μπαταρίας, εις βάρος των περιστασιακών επιβραδύνσεων που οφείλονται σε επανασύνδεση αφύπνισης.";
|
||||
"profile.sections.vpn_resolves_hostname.footer" = "Προτιμάται στα περισσότερα δίκτυα και απαιτείται σε ορισμένα δίκτυα IPv6. Απενεργοποιήστε το εκεί που μπλοκάρεται το DNS ή για να επιταχύνετε τη επικοινωνία όταν το DNS είναι αργό για να ανταποκριθεί.";
|
||||
"profile.sections.feedback.header" = "Ανατροφοδότηση";
|
||||
|
@ -178,6 +183,8 @@
|
|||
"profile.items.only_shows_favorites.caption" = "Προβολή αγαπημένων τοποθεσιών μόνο";
|
||||
"profile.items.vpn_survives_sleep.caption" = "Κρατήστε ζωντανό στον ύπνο";
|
||||
"profile.items.vpn_resolves_hostname.caption" = "Επίλυση του ονόματος σέρβερ διακομιστή";
|
||||
"profile.items.tv_sharing.caption.limited" = "Περιορίστηκε σε %d λεπτά";
|
||||
"profile.items.expires_at.caption" = "Λήξη";
|
||||
|
||||
"profile.alerts.rename.title" = "Μετονομασία προφίλ";
|
||||
"profile.alerts.reconnect_vpn.message" = "Θέλετε να συνδεθείτε ξανά με το VPN;";
|
||||
|
@ -323,6 +330,7 @@
|
|||
"paywall.items.full_version.extra_description" = "Όλοι οι πάροχοι (περιλαμβάνονται και οι μελλοντικοί)\n%@";
|
||||
"paywall.items.restore.title" = "Επαναφορά Αγορών";
|
||||
"paywall.items.restore.description" = "Εαν αγοράσατε την εφαρμογή στο παρελθόν, μπορείτε να κάνετε επαναφορά αγορών και αυτή η οθόνη δε θα εμφανιστεί ξανά.";
|
||||
"paywall.alerts.purchase.appletv.success.message" = "Σας ευχαριστούμε! Το χρονικό όριο θα αφαιρεθεί μόλις ενημερωθεί το iCloud. Περιμένετε μερικά λεπτά και μετά επανεκκινήστε τη σύνδεση στην εφαρμογή TV.";
|
||||
|
||||
/* MARK: DonateView */
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
"global.strings.authentication" = "Authentication";
|
||||
"global.messages.unlock_app" = "Passepartout is locked";
|
||||
"global.messages.email_not_configured" = "No e-mail account is configured.";
|
||||
"global.messages.share" = "Passepartout is a user-friendly, open source OpenVPN / WireGuard client for iOS and macOS";
|
||||
"global.messages.share" = "Passepartout is a user-friendly, open source OpenVPN / WireGuard client for iOS and macOS"; // FIXME: l10n, Apple platforms
|
||||
|
||||
"global.alerts.buttons.remind" = "Remind me later";
|
||||
"global.alerts.buttons.never" = "Don't ask again";
|
||||
|
@ -83,6 +83,7 @@
|
|||
"global.errors.missing_account" = "Missing account";
|
||||
"global.errors.missing_provider_server" = "Missing location";
|
||||
"global.errors.missing_provider_preset" = "Missing preset";
|
||||
"global.errors.tunnel_expired" = "Connection expired";
|
||||
|
||||
/* MARK: Menus */
|
||||
|
||||
|
@ -132,6 +133,7 @@
|
|||
/* MARK: OrganizerView */
|
||||
|
||||
"organizer.sections.active" = "In use";
|
||||
"organizer.sections.tv.profiles_list.header.p1" = "Open Passepartout on your iOS or macOS device and enable the \"Apple TV\" toggle of a profile to make it appear here.";
|
||||
/* MARK: OrganizerView */
|
||||
"organizer.empty.no_profiles" = "No profiles";
|
||||
|
||||
|
@ -163,6 +165,9 @@
|
|||
"profile.sections.vpn.footer" = "The connection will be established whenever necessary.";
|
||||
"profile.sections.status.header" = "Connection";
|
||||
"profile.sections.provider_infrastructure.footer" = "Last updated on %@.";
|
||||
"profile.sections.tv.footer.encryption" = "Profiles are encrypted and made available to your Apple TV via iCloud.";
|
||||
"profile.sections.tv.footer.restricted.p1" = "However, the connection will expire after %d minutes.";
|
||||
"profile.sections.tv.footer.restricted.p2" = "Purchase to drop the restriction.";
|
||||
"profile.sections.vpn_survives_sleep.footer" = "Disable to improve battery usage, at the expense of occasional slowdowns due to wake-up reconnections.";
|
||||
"profile.sections.vpn_resolves_hostname.footer" = "Preferred in most networks and required in some IPv6 networks. Disable where DNS is blocked, or to speed up negotiation when DNS is slow to respond.";
|
||||
"profile.sections.feedback.header" = "Feedback";
|
||||
|
@ -178,6 +183,8 @@
|
|||
"profile.items.only_shows_favorites.caption" = "Only show favorite locations";
|
||||
"profile.items.vpn_survives_sleep.caption" = "Keep alive on sleep";
|
||||
"profile.items.vpn_resolves_hostname.caption" = "Resolve provider hostname";
|
||||
"profile.items.tv_sharing.caption.limited" = "Limited to %d minutes";
|
||||
"profile.items.expires_at.caption" = "Expiration";
|
||||
|
||||
"profile.alerts.rename.title" = "Rename profile";
|
||||
"profile.alerts.reconnect_vpn.message" = "Do you want to reconnect to the VPN?";
|
||||
|
@ -323,6 +330,7 @@
|
|||
"paywall.items.full_version.extra_description" = "All providers (including future ones)\n%@";
|
||||
"paywall.items.restore.title" = "Restore purchases";
|
||||
"paywall.items.restore.description" = "If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.";
|
||||
"paywall.alerts.purchase.appletv.success.message" = "Thank you! The time limit will be dropped as soon as iCloud catches up. Wait a few moments, then restart the connection on the TV app.";
|
||||
|
||||
/* MARK: DonateView */
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
"global.strings.authentication" = "Autenticación";
|
||||
"global.messages.unlock_app" = "Passepartout está bloqueada";
|
||||
"global.messages.email_not_configured" = "Ningún e-mail configurado.";
|
||||
"global.messages.share" = "Passepartout es un cliente OpenVPN / WireGuard intuitivo, de código abierto para iOS y macOS";
|
||||
"global.messages.share" = "Passepartout es un cliente OpenVPN / WireGuard intuitivo, de código abierto para iOS y macOS"; // FIXME: l10n, Apple platforms
|
||||
|
||||
"global.alerts.buttons.remind" = "Recordar más tarde";
|
||||
"global.alerts.buttons.never" = "No preguntar más";
|
||||
|
@ -83,6 +83,7 @@
|
|||
"global.errors.missing_account" = "Sin cuenta";
|
||||
"global.errors.missing_provider_server" = "Sin ubicación";
|
||||
"global.errors.missing_provider_preset" = "Sin ajuste";
|
||||
"global.errors.tunnel_expired" = "Conexión caducada";
|
||||
|
||||
/* MARK: Menus */
|
||||
|
||||
|
@ -132,6 +133,7 @@
|
|||
/* MARK: OrganizerView */
|
||||
|
||||
"organizer.sections.active" = "En uso";
|
||||
"organizer.sections.tv.profiles_list.header.p1" = "Abre Passepartout desde tu dispositivo iOS o macOS y habilita el switch \"Apple TV\" de un perfil para que aparezca aquí abajo.";
|
||||
/* MARK: OrganizerView */
|
||||
"organizer.empty.no_profiles" = "Ningún perfil";
|
||||
|
||||
|
@ -163,6 +165,9 @@
|
|||
"profile.sections.vpn.footer" = "La conexión se establecerá siempre y cuando sea necesario.";
|
||||
"profile.sections.status.header" = "Conexión";
|
||||
"profile.sections.provider_infrastructure.footer" = "Última actualización: %@.";
|
||||
"profile.sections.tv.footer.encryption" = "Los perfiles están encriptados y puestos a disposición para tu Apple TV a través de iCloud.";
|
||||
"profile.sections.tv.footer.restricted.p1" = "Sin embargo, la conexión caducará en %d minutos.";
|
||||
"profile.sections.tv.footer.restricted.p2" = "Comprar para eliminar la restricción.";
|
||||
"profile.sections.vpn_survives_sleep.footer" = "Deshabilitar para mejorar el uso de la batería, a costa de ralentizaciones ocasionales por las reconexiones al despertar el dispositivo.";
|
||||
"profile.sections.vpn_resolves_hostname.footer" = "Preferido en la mayoría de las redes y necesario en algunas redes IPv6. Deshabilitar donde el DNS esté bloqueado, o para acelerar la negociación cuando el DNS sea lento en responder.";
|
||||
"profile.sections.feedback.header" = "Feedback";
|
||||
|
@ -178,6 +183,8 @@
|
|||
"profile.items.only_shows_favorites.caption" = "Mostrar solo ubicaciones favoritas";
|
||||
"profile.items.vpn_survives_sleep.caption" = "Mantener en modo inactivo";
|
||||
"profile.items.vpn_resolves_hostname.caption" = "Resolver hostname del servidor";
|
||||
"profile.items.tv_sharing.caption.limited" = "Limitado a %d minutos";
|
||||
"profile.items.expires_at.caption" = "Caducidad";
|
||||
|
||||
"profile.alerts.rename.title" = "Renombrar perfil";
|
||||
"profile.alerts.reconnect_vpn.message" = "Quieres reconectarte al VPN?";
|
||||
|
@ -323,6 +330,7 @@
|
|||
"paywall.items.full_version.extra_description" = "Todos los proveedores (incluye los futuros)\n%@";
|
||||
"paywall.items.restore.title" = "Restaurar compras";
|
||||
"paywall.items.restore.description" = "Si compraste esta aplicación o funcionalidad anteriormente, puedes restaurar tus compras y esta pantalla no volverá a aparecer.";
|
||||
"paywall.alerts.purchase.appletv.success.message" = "Gracias! El limite de tiempo será eliminado en cuanto iCloud se sincronice. Espera unos momentos, y reinicia la conexión en la app de la TV.";
|
||||
|
||||
/* MARK: DonateView */
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
Passepartout is a bare VPN client and, as such, does not store nor forward any personal data. Therefore, you should get in touch with your VPN service provider to learn about its data retention policy.
|
||||
|
||||
Passepartout may access GitHub repositories on request. Please refer to GitHub privacy statement for any information about data retention when accessing their website.
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
### Fixed
|
||||
### Added
|
||||
|
||||
- Persisted profile is overwritten with its former value.
|
||||
- WireGuard: Show data count.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade OpenSSL to 3.2.0.
|
||||
- Encrypt profiles stored to iCloud.
|
||||
|
||||
|
|