diff --git a/.beta-base b/.beta-base deleted file mode 100644 index 5e8d8532..00000000 --- a/.beta-base +++ /dev/null @@ -1 +0,0 @@ -1130 diff --git a/.beta-build b/.beta-build deleted file mode 100644 index 4942a239..00000000 --- a/.beta-build +++ /dev/null @@ -1 +0,0 @@ -3630 diff --git a/.github/actions/create-keychain/action.yml b/.github/actions/create-keychain/action.yml deleted file mode 100644 index 0b4cee14..00000000 --- a/.github/actions/create-keychain/action.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: "Create custom keychain" -inputs: - name: - description: "Keychain name" - required: true - password: - description: "Keychain password" - required: true -runs: - using: "composite" - steps: - - shell: bash - env: - KEYCHAIN_NAME: ${{ inputs.name }} - KEYCHAIN_PASSWORD: ${{ inputs.password }} - run: | - bundle exec fastlane run create_keychain unlock:true lock_after_timeout:false timeout:6000 - - uses: webiny/action-post-run@3.0.0 - env: - KEYCHAIN_NAME: ${{ inputs.name }} - with: - run: | - bundle exec fastlane run delete_keychain diff --git a/.github/workflows/private_beta.yml b/.github/workflows/private_beta.yml deleted file mode 100644 index 8f3172f0..00000000 --- a/.github/workflows/private_beta.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Private Beta - -on: - workflow_dispatch: - inputs: - build_number: - description: "Build number" - required: true - -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }} - FASTLANE_USERNAME: ${{ secrets.FASTLANE_USERNAME }} - FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} - BUILD_NUMBER: ${{ github.event.inputs.build_number }} - -jobs: - build_upload: - name: Distribute Private Beta - runs-on: macos-13 - environment: - name: private_beta - strategy: - fail-fast: true - matrix: - name: ["iOS", "macOS"] - include: - - name: "iOS" - platform: "ios" - - name: "macOS" - platform: "mac" - env: - PLATFORM: ${{ matrix.platform }} - MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} - MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }} - MATCH_KEYCHAIN_NAME: ${{ secrets.MATCH_KEYCHAIN_NAME }} - MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }} - GYM_OUTPUT_DIRECTORY: "dist/${{ matrix.platform }}" - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - uses: actions/setup-go@v4 - with: - go-version: "^1.17" - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: "15.1" - - name: Create keychain - uses: ./.github/actions/create-keychain - with: - name: ${{ env.MATCH_KEYCHAIN_NAME }} - password: ${{ env.MATCH_KEYCHAIN_PASSWORD }} - - name: Preinstall certificates (SSH) - run: | - scripts/ci/recognize-match-hostname.sh - bundle exec fastlane --env ${{ matrix.platform }} match development - - name: Tweak build - env: - PLIST_COMMAND: "Add :com.algoritmico.Passepartout.config:app_type integer 2" - PLIST_PATH: "Passepartout/App/Info.plist" - run: | - ci/set-build.sh $BUILD_NUMBER - /usr/libexec/PlistBuddy -c "$PLIST_COMMAND" "$PLIST_PATH" - - name: Build ${{ matrix.name }} app - timeout-minutes: 15 - run: | - bundle exec fastlane --env $PLATFORM,beta test_and_build_app test:false ensure_clean:false - - name: Submit to TestFlight - env: - PILOT_USERNAME: ${{ secrets.PILOT_USERNAME }} - PILOT_GROUPS: ${{ secrets.PILOT_GROUPS }} - PILOT_BETA_APP_FEEDBACK: ${{ secrets.PILOT_BETA_APP_FEEDBACK }} - PILOT_BETA_APP_REVIEW_INFO: ${{ secrets.PILOT_BETA_APP_REVIEW_INFO }} - PILOT_NOTIFY_EXTERNAL_TESTERS: ${{ secrets.PILOT_NOTIFY_EXTERNAL_TESTERS }} - CHANGELOG_PREFACE: ${{ secrets.CHANGELOG_PREFACE }} - run: | - if [ $PLATFORM == "ios" ]; then - export PILOT_IPA="$GYM_OUTPUT_DIRECTORY/Passepartout.ipa" - else - export PILOT_PKG="$GYM_OUTPUT_DIRECTORY/Passepartout.pkg" - fi - export PILOT_CHANGELOG=`ci/build-changelog.sh $PLATFORM` - bundle exec fastlane --env $PLATFORM,beta run pilot - - name: Tag beta - run: | - APP_VERSION=`ci/version-number.sh ios` - git tag "v$APP_VERSION-pb$BUILD_NUMBER" && git push --tags diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99fc2d13..35c5fdf2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,10 +2,8 @@ name: Release on: push: - branches: - - "master" - paths: - - ".beta-build" + tags: + - "builds/*" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -13,172 +11,47 @@ env: FASTLANE_USERNAME: ${{ secrets.FASTLANE_USERNAME }} FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build_upload: name: Upload to ASC - runs-on: macos-13 + runs-on: macos-14 strategy: fail-fast: true matrix: - name: ["iOS", "macOS", "tvOS"] + #name: ["iOS", "macOS", "tvOS"] + name: ["iOS", "macOS"] include: - name: "iOS" platform: "ios" - use_version: true - name: "macOS" - platform: "mac" - - name: "tvOS" - platform: "tvos" - env: - PLATFORM: ${{ matrix.platform }} - MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} - MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }} - GYM_OUTPUT_DIRECTORY: "dist/${{ matrix.platform }}" + platform: "macos" + #- name: "tvOS" + # platform: "tvos" steps: - - uses: actions/checkout@v3 - with: - submodules: true + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true - - uses: actions/setup-go@v4 - with: - go-version: "^1.17" - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: "latest-stable" - - name: Store app version - id: app_version - if: ${{ matrix.use_version }} - run: | - VERSION=`ci/version-number.sh $PLATFORM` - BUILD=`ci/build-number.sh $PLATFORM` - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "build=$BUILD" >> $GITHUB_OUTPUT - - name: Preinstall certificates (SSH) - run: | - scripts/ci/recognize-match-hostname.sh - bundle exec fastlane --env ${{ matrix.platform }} match development -# - name: Run tests -# run: | -# cd PassepartoutLibrary -# swift test - - name: Build ${{ matrix.name }} app - timeout-minutes: 15 - run: | - bundle exec fastlane --env $PLATFORM,beta test_and_build_app test:false - - name: Submit to TestFlight + - name: Access private repositories env: - PILOT_USERNAME: ${{ secrets.PILOT_USERNAME }} - PILOT_GROUPS: ${{ secrets.PILOT_GROUPS }} + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: | + git config --global url.https://$ACCESS_TOKEN@github.com/.insteadOf git@github.com: + - name: Upload ${{ matrix.name }} app + id: upload_app + timeout-minutes: 15 + env: + MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} + MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }} PILOT_BETA_APP_FEEDBACK: ${{ secrets.PILOT_BETA_APP_FEEDBACK }} PILOT_BETA_APP_REVIEW_INFO: ${{ secrets.PILOT_BETA_APP_REVIEW_INFO }} - PILOT_NOTIFY_EXTERNAL_TESTERS: ${{ secrets.PILOT_NOTIFY_EXTERNAL_TESTERS }} - CHANGELOG_PREFACE: ${{ secrets.CHANGELOG_PREFACE }} + PILOT_GROUPS: ${{ vars.PILOT_GROUPS }} run: | - if [ $PLATFORM == "mac" ]; then - export PILOT_PKG="$GYM_OUTPUT_DIRECTORY/Passepartout.pkg" - else - export PILOT_IPA="$GYM_OUTPUT_DIRECTORY/Passepartout.ipa" - fi - export PILOT_CHANGELOG=`ci/build-changelog.sh $PLATFORM` - bundle exec fastlane --env $PLATFORM,beta run pilot - outputs: - version: ${{ steps.app_version.outputs.version }} - build: ${{ steps.app_version.outputs.build }} - distribute_public_beta: - name: Distribute Public Beta - runs-on: ubuntu-latest - needs: build_upload - environment: - name: public_beta - url: "https://testflight.apple.com/join/K71mtLjZ" - env: - PILOT_APP_VERSION: ${{ needs.build_upload.outputs.version }} - PILOT_BUILD_NUMBER: ${{ needs.build_upload.outputs.build }} - PILOT_USERNAME: ${{ secrets.PILOT_USERNAME }} - PILOT_GROUPS: ${{ secrets.PILOT_GROUPS }} - PILOT_NOTIFY_EXTERNAL_TESTERS: ${{ secrets.PILOT_NOTIFY_EXTERNAL_TESTERS }} - PILOT_DISTRIBUTE_ONLY: true - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - name: Publish on TestFlight (iOS) - run: | - bundle exec fastlane --env ios,beta run pilot - - name: Publish on TestFlight (macOS) - run: | - bundle exec fastlane --env mac,beta run pilot - - name: Publish on TestFlight (tvOS) - run: | - bundle exec fastlane --env tvos,beta run pilot - submit_for_app_review: - name: Submit to App Review - runs-on: ubuntu-latest - needs: build_upload - environment: - name: app_review - env: - DELIVER_USERNAME: ${{ secrets.DELIVER_USERNAME }} - DELIVER_APP_VERSION: ${{ needs.build_upload.outputs.version }} - DELIVER_BUILD_NUMBER: ${{ needs.build_upload.outputs.build }} - DELIVER_FORCE: true - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - name: Submit for App Review (iOS) - run: | - bundle exec fastlane --env ios deliver_review add_id_info_uses_idfa:false - - name: Submit for App Review (macOS) - run: | - bundle exec fastlane --env mac deliver_review add_id_info_uses_idfa:false - - name: Submit for App Review (tvOS) - run: | - bundle exec fastlane --env tvos deliver_review add_id_info_uses_idfa:false - publish_to_app_store: - name: Publish to App Store - runs-on: ubuntu-latest - needs: [build_upload, submit_for_app_review] - environment: - name: app_store - env: - TAG_NAME: ${{ needs.build_upload.outputs.version }} - RELEASE_NOTES: release-notes.txt - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5 - with: - gpg_private_key: ${{ secrets.GPG_KEY }} - passphrase: ${{ secrets.GPG_PASSPHRASE }} - git_user_signingkey: true - git_commit_gpgsign: true - git_tag_gpgsign: true - git_push_gpgsign: false - - name: Tag release - run: | - scripts/ci/tag-release.sh $TAG_NAME - git push --tags - - name: Assemble notes - run: | - scripts/ci/release-notes.sh $TAG_NAME >$RELEASE_NOTES - - name: Publish release - uses: softprops/action-gh-release@v1 - with: - tag_name: "v${{ env.TAG_NAME }}" - body_path: ${{ env.RELEASE_NOTES }} - draft: true - files: | - ${{ env.RELEASE_NOTES }} + ci/recognize-match-hostname.sh + bundle exec fastlane --env ${{ matrix.platform }} beta diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fbbfffd7..9c86922a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,36 +4,35 @@ on: pull_request: types: [ opened, synchronize ] paths-ignore: - - '.beta-*' - '.env.*' - '**/*.md' - '**/*.sh' - '**/*.yml' - - 'Passepartout/App/fastlane/**' + - 'fastlane/**' concurrency: - group: ${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: run_tests: name: Run tests - runs-on: macos-13 + runs-on: macos-14 timeout-minutes: 15 steps: - - uses: actions/checkout@v3 - with: - submodules: true - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: "15.1" + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true - - uses: actions/setup-go@v4 + - uses: maxim-lobanov/setup-xcode@v1 with: - go-version: "^1.17" + xcode-version: 15.4 + - name: Access private repositories + env: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: | + git config --global url.https://$ACCESS_TOKEN@github.com/.insteadOf git@github.com: - name: Run tests run: | - cd PassepartoutLibrary + cd Passepartout/Library swift test diff --git a/.gitignore b/.gitignore index 55eb9068..82536f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,21 +2,19 @@ *.swp *.pbxuser **/xcuserdata -Pods **/fastlane/**/*.html **/fastlane/README.md **/fastlane/report.xml **/fastlane/test_output **/fastlane/*/metadata/review_information **/fastlane/*/metadata/trade_representative_contact_information -dist/ -iap/ -templates/ -.env.secret* -Preview.html -passepartout-translations.zip -default.profraw -asc-key.json -.bundle -vendor/ build/ +dist/ +/iap +templates/ +vendor/ +Preview.html +default.profraw +.build +.bundle +.env.secret* diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 960d6f33..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "PassepartoutLibrary/Sources/PassepartoutServices/API"] - path = PassepartoutLibrary/Sources/PassepartoutProvidersImpl/API - url = https://github.com/passepartoutvpn/api diff --git a/.swiftlint.yml b/.swiftlint.yml index ed64d074..0e938f13 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,7 +1,9 @@ included: - - Passepartout - - PassepartoutLibrary/Sources - - PassepartoutLibrary/Tests + - Passepartout/App + - Passepartout/Library/Sources + - Passepartout/Library/Tests + - Passepartout/Shared + - Passepartout/Tunnel analyzer_rules: - unused_declaration - unused_import @@ -14,5 +16,6 @@ disabled_rules: - function_body_length - identifier_name - line_length + - inclusive_language - nesting - todo diff --git a/API b/API deleted file mode 120000 index a8ad7531..00000000 --- a/API +++ /dev/null @@ -1 +0,0 @@ -PassepartoutLibrary/Sources/PassepartoutProvidersImpl/API/ \ No newline at end of file diff --git a/CHANGELOG.1.ios.md b/CHANGELOG.1.ios.md deleted file mode 100644 index 63e9f658..00000000 --- a/CHANGELOG.1.ios.md +++ /dev/null @@ -1,520 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 1.18.0 (2022-02-15) - -### Added - -- Handle `--keepalive` option. - -### Changed - -- Release app in the open via GitHub Actions. - -### Fixed - -- Trim whitespaces in text fields. - -## 1.17.2 (2021-11-30) - -### Changed - -- Revert to OpenSSL. - -### Fixed - -- "TLS failed" with some certificates (e.g. Let's Encrypt). -- Newer infrastructure discarded over bundle. - -## 1.17.0 (2021-11-16) - -### Changed - -- Replace OpenSSL with BoringSSL. -- Restrict support to secure TLS algorithms (security level). -- Allow Oeck provider without any purchase. - -### Fixed - -- iOS 15: Navigation bar has broken appearance. -- Missing account guidance footer in some providers. -- Files imported via Music (iTunes) File Sharing did not show up. - -## 1.16.0 (2021-08-09) - -### Added - -- Support for `--scramble xormask`. [tunnelkit#38](https://github.com/passepartoutvpn/tunnelkit/issues/38) -- Oeck provider. - -## 1.15.4 (2021-07-21) - -### Added - -- SurfShark provider. -- Support for `--compress stub-v2`. - -## 1.15.2 (2021-04-17) - -### Changed - -- Drop Twitch link. - -## 1.15.1 (2021-02-14) - -### Fixed - -- No way to set DNS servers when using DNS over HTTPS. [#171](https://github.com/passepartoutvpn/passepartout-apple/issues/171) - -## 1.15.0 (2021-02-09) - -### Added - -- Support `--data-ciphers` from OpenVPN 2.5. [tunnelkit#193](https://github.com/passepartoutvpn/tunnelkit/issues/193) -- Support DNS over HTTPS/TLS in "Network settings". [#91](https://github.com/passepartoutvpn/passepartout-apple/issues/91) - -### Changed - -- Drop hosts restriction in free version ("Unlimited hosts"). - -### Fixed - -- Redundant keychain items. -- Keyboard not dismissed in "Network settings". -- "Reset configuration" not working with encrypted configuration files. -- "Update list" locks up in providers. - -## 1.14.0 (2021-01-07) - -### Added - -- Can now copy entries in "Server network". - -### Changed - -- Rendering of provider infrastructures. -- Default to low MTU (1200) when unspecified. - -## 1.13.1 (2021-01-03) - -### Fixed - -- Losing profiles on upgrade. [#163](https://github.com/passepartoutvpn/passepartout-ios/issues/163) -- Twitch link does not work when Twitch app not installed. [#162](https://github.com/passepartoutvpn/passepartout-ios/issues/162) - -## 1.13.0 (2021-01-01) - -### Added - -- Customize MTU in network settings. - -### Changed - -- Enter explicit Wi-Fi SSID to trust. -- Use default tunnel MTU rather than 1250. - -## 1.12.1 (2020-11-15) - -### Added - -- Watch me make Passepartout live on Twitch. - -## 1.12.0 (2020-10-06) - -### Added - -- Child Safe VPN provider. - -### Changed - -- Improved host import flow. -- Use active profile name in iOS settings. - -### Fixed - -- In-app purchases may not be credited/restored (Radu Ursache). [#153](https://github.com/passepartoutvpn/passepartout-ios/issues/153) - -## 1.11.5 (2020-06-23) - -### Fixed - -- Skip DNS resolution of provider servers without a hostname (e.g. ProtonVPN "Secure Core"). - -## 1.11.4 (2020-06-03) - -### Added - -- Customize host endpoint. - -### Fixed - -- Invisible buttons in document browser. [#145](https://github.com/passepartoutvpn/passepartout-ios/issues/145) - -## 1.11.3 (2020-05-21) - -### Added - -- TorGuard provider (Jorrit Visser). [api-source#5](https://github.com/passepartoutvpn/api-source/issues/5) - -### Fixed - -- Persistent crash on launch after "Add new provider > Update list". - -## 1.11.2 (2020-05-12) - -### Changed - -- Relax keyboard for host titles. - -### Fixed - -- In-app purchase unavailable for new providers. [#141](https://github.com/passepartoutvpn/passepartout-ios/issues/141) -- Hosts may be renamed to same title. [#140](https://github.com/passepartoutvpn/passepartout-ios/issues/140) - -## 1.11.1 (2020-05-11) - -### Added - -- Hide.me provider. - -## 1.11.0 (2020-04-29) - -### Changed - -- Allow any character in host profile name. [#26](https://github.com/passepartoutvpn/passepartout-ios/issues/26) - -### Fixed - -- Programming error in some SoftEther negotiation (Grivus). [tunnelkit#143](https://github.com/passepartoutvpn/tunnelkit/pull/143) -- Default gateway not yet enforced for providers (e.g. TunnelBear). [passepartout-core-apple#4](https://github.com/passepartoutvpn/passepartout-core-apple/pull/4) -- Active profile lost after renaming. [#128](https://github.com/passepartoutvpn/passepartout-ios/issues/128) -- Handle server shutdown/restart (remote `--explicit-exit-notify`). [tunnelkit#131](https://github.com/passepartoutvpn/tunnelkit/issues/131) -- Handle explicit IPv4/IPv6 protocols (`4` or `6` suffix in `--proto`). [tunnelkit#153](https://github.com/passepartoutvpn/tunnelkit/issues/153) -- IPv6 traffic broken on Mojave. [tunnelkit#146](https://github.com/passepartoutvpn/tunnelkit/issues/146), [#169](https://github.com/passepartoutvpn/tunnelkit/pull/169) -- Transient connected state upon connection failure (rob-patchett). [tunnelkit#128](https://github.com/passepartoutvpn/tunnelkit/pull/128) - -## 1.10.1 (2019-12-24) - -### Fixed - -- Provider purchases were not properly recognized/restored. [#124](https://github.com/passepartoutvpn/passepartout-ios/pull/124) - -## 1.10.0 (2019-12-19) - -### Added - -- Dynamic providers, refresh supported list in real time. -- Favorite provider locations. [#118](https://github.com/passepartoutvpn/passepartout-ios/issues/118) -- Polish translations (Piotr Książek). - -### Changed - -- "Trusted networks" settings are now saved per profile. [#114](https://github.com/passepartoutvpn/passepartout-ios/issues/114) -- Require explicit `--ca` and `--cipher` in .ovpn configuration file. -- Revert fallback to CloudFlare DNS when no servers provided. [#116](https://github.com/passepartoutvpn/passepartout-ios/issues/116) -- German translations (Theodor Tietze). - -### Fixed - -- Only show pushed server configuration. -- Adjust UI to device text size. [#117](https://github.com/passepartoutvpn/passepartout-ios/pull/117) -- Restore provider flow after purchase. -- Improved some translations. - -## 1.9.1 (2019-11-10) - -### Changed - -- Polish purchase screen. - -## 1.9.0 (2019-11-05) - -### Added - -- Import host via document picker. -- Support for `--ping-restart` (Robert Patchett). [tunnelkit#122](https://github.com/passepartoutvpn/tunnelkit/pull/122) -- Support for proxy auto-configuration URL (ThinkChaos). [tunnelkit#125](https://github.com/passepartoutvpn/tunnelkit/pull/125) -- Disclose server configuration and network settings in Diagnostics. [#101](https://github.com/passepartoutvpn/passepartout-ios/issues/101) -- Support multiple DNS search domains. [tunnelkit#127](https://github.com/passepartoutvpn/tunnelkit/issues/127) - -### Changed - -- Upgrade project to Xcode 11. - -### Fixed - -- Cannot enter IP addresses in some localizations. [#103](https://github.com/passepartoutvpn/passepartout-ios/issues/103) -- Cannot easily trust Wi-Fi networks in iOS 13. [#100](https://github.com/passepartoutvpn/passepartout-ios/issues/100) -- Infrastructures not updated in non-English locales. -- Default gateway not enforced for providers (e.g. TunnelBear). - -## 1.8.1 (2019-09-15) - -### Added - -- Chinese (Simplified) translations (OnlyThen). [#95](https://github.com/passepartoutvpn/passepartout-ios/pull/95) -- Support for iOS 13 Dark Mode. [#93](https://github.com/passepartoutvpn/passepartout-ios/issues/93) - -### Fixed - -- Transparent navigation bar in iPadOS 13. -- Unable to open .ovpn files in iOS 13. [#99](https://github.com/passepartoutvpn/passepartout-ios/issues/99) -- Premature disconnection due to .staleSession error. [tunnelkit#120](https://github.com/passepartoutvpn/tunnelkit/issues/120) - -## 1.8.0 (2019-08-01) - -### Added - -- "Custom DNS" preset for Mullvad. [api-source-mullvad#1](https://github.com/passepartoutvpn/api-source-mullvad/issues/1) -- Change app language from Settings in iOS 13. [#90](https://github.com/passepartoutvpn/passepartout-ios/issues/90) - -### Changed - -- Disconnect on "No buffer space available" rather than leaving a stale connection (improve later). [tunnelkit#104](https://github.com/passepartoutvpn/tunnelkit/issues/104) - -### Fixed - -- VPN staying active while it's not. [tunnelkit#106](https://github.com/passepartoutvpn/tunnelkit/issues/106) -- Disconnection on renegotiation. [tunnelkit#105](https://github.com/passepartoutvpn/tunnelkit/issues/105) -- Support third party apps when sending e-mails. -- Refreshed infrastructures are not retained. [passepartout-core-apple#1](https://github.com/passepartoutvpn/passepartout-core-apple/issues/1) -- Portuguese bound to Brazil region. -- German spelling of "Default gateway". -- Some French wording (Joel Gallant). -- Erroneous placeholders in Network Settings (Joel Gallant). - -## 1.7.0 (2019-06-02) - -### Added - -- Dutch translations (Norbert de Vreede). [#81](https://github.com/passepartoutvpn/passepartout-ios/pull/81) -- Greek translations (Konstantinos Koukoulakis). -- French translations (Julien Laniel). -- Spanish translations (Davide De Rosa, Elena Vivó). -- Swedish translations (Henry Gross-Hellsen). [#82](https://github.com/passepartoutvpn/passepartout-ios/pull/82) - -## 1.6.1 (2019-05-20) - -### Added - -- Override network settings. [#77](https://github.com/passepartoutvpn/passepartout-ios/pull/77) -- Support for `--redirect-gateway block-local` (partial). [tunnelkit#81](https://github.com/passepartoutvpn/tunnelkit/issues/81) -- Russian translations (Alexander Korobynikov). - -### Changed - -- Host compression framing and algorithm are now editable. - -### Fixed - -- NordVPN double servers not connecting out of the box. [#78](https://github.com/passepartoutvpn/passepartout-ios/issues/78) -- Authentication with OpenVPN AS. [tunnelkit#95](https://github.com/passepartoutvpn/tunnelkit/issues/95) -- TLS failed with some servers. [tunnelkit#97](https://github.com/passepartoutvpn/tunnelkit/issues/97) - -## 1.6.0 (2019-05-01) - -### Added - -- VyprVPN provider. [#72](https://github.com/passepartoutvpn/passepartout-ios/pull/72) -- More infrastructure metadata. -- Portuguese translations (Helder Santana). [#70](https://github.com/passepartoutvpn/passepartout-ios/pull/70) -- German translations (Christian Lederer). -- Russian translations (Alexander Korobynikov). - -### Changed - -- Do not redirect all traffic to VPN unless `--redirect-gateway` specified. [#71](https://github.com/passepartoutvpn/passepartout-ios/pull/71) - -### Fixed - -- Fall back to CloudFlare DNS when no servers provided. [tunnelkit#84](https://github.com/passepartoutvpn/tunnelkit/issues/84) -- UDP may disconnect on high speeds. [tunnelkit#87](https://github.com/passepartoutvpn/tunnelkit/issues/87) -- SoftEther connects without VPN icon. [#69](https://github.com/passepartoutvpn/passepartout-ios/issues/69) -- Misleading Mullvad password suggestion. [#75](https://github.com/passepartoutvpn/passepartout-ios/issues/75) -- Leave digest editable despite cipher. [#74](https://github.com/passepartoutvpn/passepartout-ios/issues/74) -- TLS errors with passphrase-protected .ovpn profiles. [tunnelkit#91](https://github.com/passepartoutvpn/tunnelkit/issues/91) -- Issue with DNS-only VPN profiles. [#73](https://github.com/passepartoutvpn/passepartout-ios/issues/73) - -## 1.5.0 (2019-04-17) - -### Added - -- NordVPN provider. [#65](https://github.com/passepartoutvpn/passepartout-ios/pull/65) -- Support for `dhcp-option PROXY_HTTP[S]`. [tunnelkit#74](https://github.com/passepartoutvpn/tunnelkit/issues/74) - -### Fixed - -- Regression in DNS configuration. [#68](https://github.com/passepartoutvpn/passepartout-ios/issues/68) -- SoftEther timing out. [tunnelkit#67](https://github.com/passepartoutvpn/tunnelkit/issues/67) -- VPN status cell doesn't always enter active profile. [#63](https://github.com/passepartoutvpn/passepartout-ios/issues/63) -- Masking preference not retained. [#64](https://github.com/passepartoutvpn/passepartout-ios/issues/64) -- Issues with very long PUSH_REPLY. [tunnelkit#71](https://github.com/passepartoutvpn/tunnelkit/issues/71) -- Missing app icon in Credits. - -## 1.4.0 (2019-04-11) - -### Added - -- ProtonVPN provider. [#7](https://github.com/passepartoutvpn/passepartout-ios/issues/7) -- Italian translations. [#58](https://github.com/passepartoutvpn/passepartout-ios/pull/58) -- In-app donations. -- Provider logos. [#55](https://github.com/passepartoutvpn/passepartout-ios/pull/55) -- Country flags. [#56](https://github.com/passepartoutvpn/passepartout-ios/pull/56) -- VPN status shortcut, enters active profile on selection. - -### Changed - -- Automatic protocol defaults to UDP endpoints. [#61](https://github.com/passepartoutvpn/passepartout-ios/pull/61) -- Improved Account screen, footers were hardly tappable. - -### Fixed - -- Some providers may crash on VPN activation. [#57](https://github.com/passepartoutvpn/passepartout-ios/issues/57) -- Mullvad dying due to ping timeout. [#62](https://github.com/passepartoutvpn/passepartout-ios/issues/62) -- Pushing DOMAIN has no effect. [#48](https://github.com/passepartoutvpn/passepartout-ios/issues/48) - -## 1.3.0 (2019-04-03) - -### Added - -- Windscribe provider. [#39](https://github.com/passepartoutvpn/passepartout-ios/issues/39) - -### Fixed - -- Support PKCS#8 encrypted cert keys. [#43](https://github.com/passepartoutvpn/passepartout-ios/issues/43) -- Handle PEM with preamble. [tunnelkit#78](https://github.com/passepartoutvpn/tunnelkit/issues/78) -- Infrastructures not retained after refresh. [#54](https://github.com/passepartoutvpn/passepartout-ios/issues/54) - -## 1.2.0 (2019-04-01) - -### Added - -- Siri Shortcuts in-app manager. [#46](https://github.com/passepartoutvpn/passepartout-ios/pull/46) -- Background data count updates in diagnostics. [#51](https://github.com/passepartoutvpn/passepartout-ios/pull/51) -- Configure masking in debug log for improved diagnostics. -- Mullvad provider. [#45](https://github.com/passepartoutvpn/passepartout-ios/pull/45) -- Support for encrypted certificate private keys. [#43](https://github.com/passepartoutvpn/passepartout-ios/pull/43) - -### Changed - -- Upgraded to Swift 5. - -### Fixed - -- EKU not verified with providers (regression). -- Occasionally overlapping footers in organizer. - -## 1.1.0 (2019-03-22) - -### Added - -- Support for LZO compression. [#32](https://github.com/passepartoutvpn/passepartout-ios/issues/32) -- Siri shortcuts. [#41](https://github.com/passepartoutvpn/passepartout-ios/pull/41) -- Custom intents, have a look at Spotlight suggestions for Passepartout. [#40](https://github.com/passepartoutvpn/passepartout-ios/pull/40) -- TunnelBear provider. [#35](https://github.com/passepartoutvpn/passepartout-ios/pull/35) - -### Changed - -- Normalize localization of provider locations. - -### Fixed - -- Profile not activating if none is active. [#42](https://github.com/passepartoutvpn/passepartout-ios/issues/42) -- EKU verification enabled when it shouldn't be. -- Incorrect VPN status after renaming. [#37](https://github.com/passepartoutvpn/passepartout-ios/issues/37) -- Profile change doesn't disconnect active VPN. [#38](https://github.com/passepartoutvpn/passepartout-ios/issues/38) -- Some reconnection issues encountered with TunnelBear and NordVPN. -- Hosts gone while connected (credit to Aston Martin). [#19](https://github.com/passepartoutvpn/passepartout-ios/issues/19) - -## 1.0.3 (2019-03-06) - -### Fixed - -- Regression in profile activation. [#36](https://github.com/passepartoutvpn/passepartout-ios/issues/36) - -## 1.0.2 (2019-03-04) - -### Fixed - -- Profile sometimes not connecting right after add. -- Custom DNS servers were not applied. -- Shut down if server uses compression at all. -- Broken link to SwiftGen license. - -## 1.0.1 (2019-02-27) - -### Added - -- Override DNS servers via `dhcp-option DNS`. [tunnelkit#56](https://github.com/passepartoutvpn/tunnelkit/pull/56) -- About link to FAQ. - -### Changed - -- Only enable EKU verification if `remote-cert-tls server`. [tunnelkit#64](https://github.com/passepartoutvpn/tunnelkit/pull/64) - -### Fixed - -- Shut down if server pushes a compression directive. [tunnelkit#65](https://github.com/passepartoutvpn/tunnelkit/pull/65) -- Retain DNS reply order in resolved endpoint addresses. [#31](https://github.com/passepartoutvpn/passepartout-ios/pull/31) - -## 1.0 (2019-01-16) - -### Added - -- Automated app rating mechanism. -- Dot as a legal character in host profile title. [#22](https://github.com/passepartoutvpn/passepartout-ios/issues/22) -- Host profiles can now be renamed. [#24](https://github.com/passepartoutvpn/passepartout-ios/issues/24) -- Explicit rejection of encrypted client certificate keys. [#15](https://github.com/passepartoutvpn/passepartout-ios/issues/15) -- Attach .ovpn when reporting a connectivity issue, stripped of sensitive data. [#13](https://github.com/passepartoutvpn/passepartout-ios/pull/13) -- iTunes File Sharing (skythedesu). [#14](https://github.com/passepartoutvpn/passepartout-ios/pull/14) -- Tunnel failure reporting in UI. [#8](https://github.com/passepartoutvpn/passepartout-ios/pull/8) -- Explicit "Reconnect" button. [#9](https://github.com/passepartoutvpn/passepartout-ios/pull/9) -- Option to revert host parameters to original configuration (Nicholas Caito). [#10](https://github.com/passepartoutvpn/passepartout-ios/pull/10) -- Support for TLS wrapping (tls-auth and tls-crypt). [#5](https://github.com/passepartoutvpn/passepartout-ios/pull/5) -- AES-GCM and new endpoints to PIA network preset. [tunnelkit#32](https://github.com/passepartoutvpn/tunnelkit/pull/32) -- Disclosure indicators in profile organizer (Samuel Michaels). -- Disclaimer for app usage. - -### Removed - -- "Test connectivity" until it's more transparent. -- Password confirmation field, redundant with authentication failure message. - -### Changed - -- Relocated API endpoints, better before first release. -- Reorganized credits page. -- Internal refactoring (nothing visible). -- Disconnect VPN by default when entering a trusted network. [#25](https://github.com/passepartoutvpn/passepartout-ios/pull/25) -- Host parameters are read-only if there isn't an original configuration to revert to. -- Overall serialization performance. -- Drive generic support requests on Reddit. -- Add current Wi-Fi to trusted networks list but don't trust it by default. - -### Fixed - -- Infrastructures not refreshed. [#29](https://github.com/passepartoutvpn/passepartout-ios/issues/29) -- Incorrect compression warnings when importing host configurations. [#20](https://github.com/passepartoutvpn/passepartout-ios/pull/20) -- Regression in provider endpoints, IPv4 appearing reversed. [#23](https://github.com/passepartoutvpn/passepartout-ios/pull/23) -- Handling of extra whitespaces in .ovpn (Mike Mayer). [#17](https://github.com/passepartoutvpn/passepartout-ios/issues/17) -- Glitches in import wizard flow, sometimes not even appearing. -- Warn about .ovpn containing potentially unsupported compression. [#16](https://github.com/passepartoutvpn/passepartout-ios/issues/16) -- Retain credentials of replaced host profile. -- Original configuration not saved after reset. -- Connection occasionally turning inactive after a while. -- Improved performance and privacy of debug log. -- .ovpn files could not be imported without OpenVPN Connect installed. [#6](https://github.com/passepartoutvpn/passepartout-ios/issues/6) -- Fixed Mullvad abrupt disconnection. [tunnelkit#30](https://github.com/passepartoutvpn/tunnelkit/issues/30) -- Credentials are now optional for host profiles. [#4](https://github.com/passepartoutvpn/passepartout-ios/pull/4) -- Can now import .ovpn files from Apple Files app. [#1](https://github.com/passepartoutvpn/passepartout-ios/pull/1) -- Reject unrecognized values for `cipher`, `auth` and `proto`. [#1](https://github.com/passepartoutvpn/passepartout-ios/pull/1) -- Alert unsupported configuration options. -- Use accent color for checkmarks in table cells. - -## 1.0 beta 975 (2018-10-11) - -First public beta release. diff --git a/CHANGELOG.1.mac.md b/CHANGELOG.1.mac.md deleted file mode 100644 index 9fb6cb63..00000000 --- a/CHANGELOG.1.mac.md +++ /dev/null @@ -1,149 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 1.18.0 (2022-02-15) - -### Added - -- Handle `--keepalive` option. - -### Changed - -- Release app in the open via GitHub Actions. - -### Fixed - -- Last update was not refreshed on "Refresh infrastructure". -- Trim whitespaces in text fields. - -## 1.17.2 (2021-11-30) - -### Changed - -- Revert to OpenSSL. - -### Fixed - -- "TLS failed" with some certificates (e.g. Let's Encrypt). -- Newer infrastructure discarded over bundle. - -## 1.17.0 (2021-11-16) - -### Changed - -- Replace OpenSSL with BoringSSL. -- Restrict support to secure TLS algorithms (security level). -- Drop status bar icon color to automatically adjust to desktop background color. [#199](https://github.com/passepartoutvpn/passepartout-apple/issues/199) -- Allow Oeck provider without any purchase. - -### Fixed - -- Location areas were not sorted in menu. - -## 1.16.0 (2021-08-09) - -### Added - -- Support for `--scramble xormask`. [tunnelkit#38](https://github.com/passepartoutvpn/tunnelkit/issues/38) -- Favorite provider locations. -- Oeck provider. -- In-app donations. - -## 1.15.3 (2021-07-20) - -### Added - -- SurfShark provider. -- Support for `--compress stub-v2`. - -### Fixed - -- Crash when adding dynamically updated provider. -- In-app purchases might crash the app and not be credited until relaunch. - -## 1.15.2 (2021-04-17) - -### Added - -- Website guidance in provider account screen. -- Missing translations (German, Greek, Spanish, French, Dutch, Polish, Portuguese, Russian, Swedish, Chinese Simplified). - -### Changed - -- Improve debug log appearance. - -### Fixed - -- Prevent ineffective editing of trusted network SSID. -- VPN not being disabled when "Inactive" due to trusted network. - -## 1.15.1 (2021-02-14) - -### Changed - -- Skip keychain password prompt. [tunnelkit#200](https://github.com/passepartoutvpn/tunnelkit/issues/200) - -### Fixed - -- No way to set DNS servers when using DNS over HTTPS. [#171](https://github.com/passepartoutvpn/passepartout-apple/issues/171) - -## 1.15.0 (2021-02-09) - -### Added - -- Support `--data-ciphers` from OpenVPN 2.5. [tunnelkit#193](https://github.com/passepartoutvpn/tunnelkit/issues/193) -- Support DNS over HTTPS/TLS in "Network settings". [#91](https://github.com/passepartoutvpn/passepartout-apple/issues/91) -- Menu tooltip describing active profile and status. -- Make "Confirm quit" a preference. - -### Changed - -- Rendering of profile configuration. -- Color-blind friendly menu icon. - -### Fixed - -- Missing PAC URL in proxy settings. -- Redundant keychain items. - -## 1.14.0 (2021-01-07) - -### Added - -- Country flags in provider infrastructure menu. - -### Changed - -- Rendering of provider infrastructures. - -### Fixed - -- Provider infrastructure selectors not reloaded on profile change. - -## 1.0.0 (2021-01-01) - -### Added - -- Launch on boot/login. -- Change active profile from menu. -- Edit credentials/profile from menu. -- Links in About dialog. - -### Changed - -- Mimic iOS app when activating a profile (Use then Enable). -- Do not autoconnect to selected location. - -### Fixed - -- Unsaved settings. -- Incorrect keychain management. -- Menu inconsistencies. - -## 1.0.0 beta 345 (2018-10-01) - -First private beta release. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 476a8378..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,169 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 2.3.6 (2024-09-22) - -### Fixed - -- Restricted profile not updated. [#481](https://github.com/passepartoutvpn/passepartout-apple/pull/481) -- Selection and switch have the same color in organizer. [#458](https://github.com/passepartoutvpn/passepartout-apple/issues/458), [#486](https://github.com/passepartoutvpn/passepartout-apple/pull/486), [#490](https://github.com/passepartoutvpn/passepartout-apple/pull/490) - -## 2.3.5 (2024-01-19) - -### Fixed - -- Minor stuff. - -## 2.3.4 (2024-01-14) - -### Fixed - -- Unintended sensitive data in issue reports. [#471](https://github.com/passepartoutvpn/passepartout-apple/pull/471) - -## 2.3.3 (2024-01-11) - -### Fixed - -- Platform purchasers cannot upgrade to full version. [#464](https://github.com/passepartoutvpn/passepartout-apple/issues/464) - -## 2.3.2 (2024-01-11) - -### Fixed - -- "Restore purchases" not working. [#459](https://github.com/passepartoutvpn/passepartout-apple/issues/459) -- Purchase is not credited if any refund was issued in the past. [#461](https://github.com/passepartoutvpn/passepartout-apple/issues/461) -- On-demand not applying to wired connections. [#463](https://github.com/passepartoutvpn/passepartout-apple/pull/463) - -## 2.3.1 (2024-01-06) - -### Fixed - -- OpenVPN: Regressions from the upgrade to OpenSSL 3. [tunnelkit#403](https://github.com/passepartoutvpn/tunnelkit/issues/403) - -## 2.3.0 (2023-12-31) - -### 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 - -- Upgrade OpenSSL to 3.2.0. [tunnelkit#336](https://github.com/passepartoutvpn/tunnelkit/issues/336) -- Encrypt profiles stored to iCloud. [#436](https://github.com/passepartoutvpn/passepartout-apple/pull/436) - -## 2.2.1 (2023-10-14) - -### Fixed - -- Persisted profile is overwritten with its former value. [#367](https://github.com/passepartoutvpn/passepartout-apple/issues/367) - -## 2.2.0 (2023-10-10) - -### Added - -- OpenVPN: Allow editing of endpoints. [#335](https://github.com/passepartoutvpn/passepartout-apple/pull/335) - -### Changed - -- Make iCloud an opt-in preference. [#227](https://github.com/passepartoutvpn/passepartout-apple/issues/227) -- OpenVPN: Endpoint UX. [#332](https://github.com/passepartoutvpn/passepartout-apple/pull/332) -- Convert trusted networks to on demand activation. [#119](https://github.com/passepartoutvpn/passepartout-apple/issues/119) - -## 2.1.2 (2023-07-06) - -### Fixed - -- Allow wildcards in proxy bypass domains. [#296](https://github.com/passepartoutvpn/passepartout-apple/issues/296) -- Fail gracefully when refreshing infrastructure. [#307](https://github.com/passepartoutvpn/passepartout-apple/pull/307) -- Only show 'Reconnect' on active profile. [#311](https://github.com/passepartoutvpn/passepartout-apple/pull/311) -- IPv4/6 address validation. [#308](https://github.com/passepartoutvpn/passepartout-apple/pull/308) -- Domain name validation. [#297](https://github.com/passepartoutvpn/passepartout-apple/pull/297) - -## 2.1.1 (2023-04-19) - -### Added - -- Show app version in Mac menu (macOS). [#286](https://github.com/passepartoutvpn/passepartout-apple/pull/286) - -### Fixed - -- Roll back broken kill switch flag. [#294](https://github.com/passepartoutvpn/passepartout-apple/pull/294) -- Remove nonsense Mac menus (macOS). [#285](https://github.com/passepartoutvpn/passepartout-apple/pull/285) - -## 2.1.0 (2023-04-07) - -### Added - -- Option to lock app when entering background (iOS). [#270](https://github.com/passepartoutvpn/passepartout-apple/pull/270) -- 3D Touch items (iOS). [#267](https://github.com/passepartoutvpn/passepartout-apple/pull/267) -- Ukranian translations (@josser). [#243](https://github.com/passepartoutvpn/passepartout-apple/pull/243) -- Randomize provider server. [#263](https://github.com/passepartoutvpn/passepartout-apple/pull/263) -- Restore DNS "Domain" setting. [#260](https://github.com/passepartoutvpn/passepartout-apple/pull/260) -- OpenVPN: Full implementation of Tunnelblick XOR patch (@tmthecoder). [#245](https://github.com/passepartoutvpn/passepartout-apple/pull/245), [tunnelkit#255](https://github.com/passepartoutvpn/tunnelkit/pull/255) -- WireGuard: DoH/DoT options. [#264](https://github.com/passepartoutvpn/passepartout-apple/pull/264) - -### Changed - -- Bump targets to iOS 15 / macOS 12. -- Always show "Reconnect" button. [#277](https://github.com/passepartoutvpn/passepartout-apple/pull/277) -- Move Diagnostics view to Profile bottom. [#261](https://github.com/passepartoutvpn/passepartout-apple/pull/261) - -### Fixed - -- Improve kill switch behavior. [#181](https://github.com/passepartoutvpn/passepartout-apple/issues/181) -- Retain original filename as imported profile name. [#240](https://github.com/passepartoutvpn/passepartout-apple/pull/240) -- In-app purchases other than full version were not recognized (macOS). [#281](https://github.com/passepartoutvpn/passepartout-apple/pull/281) - -## 2.0.2 (2022-10-31) - -### Added - -- OpenVPN: Support for `--remote-random-hostname`. [tunnelkit#286](https://github.com/passepartoutvpn/tunnelkit/pull/286) - -### Fixed - -- OpenVPN: Tunnel dying prematurely. [tunnelkit#289](https://github.com/passepartoutvpn/tunnelkit/issues/289), [#237](https://github.com/passepartoutvpn/passepartout-apple/issues/237) -- OpenVPN: Local network settings being ignored. [tunnelkit#290](https://github.com/passepartoutvpn/tunnelkit/issues/290) -- OpenVPN: Routes from configuration file are ignored. [tunnelkit#278](https://github.com/passepartoutvpn/tunnelkit/issues/278) -- OpenVPN: Parse IPv6 endpoints properly. [tunnelkit#294](https://github.com/passepartoutvpn/tunnelkit/issues/294) -- Restore "Reconnect" action in profiles. [#232](https://github.com/passepartoutvpn/passepartout-apple/pull/232) -- Systematic uninstallation of VPN profile if any IAP was refunded. [#238](https://github.com/passepartoutvpn/passepartout-apple/issues/238) -- Use .includeAllNetworks for best-effort kill switch. [#181](https://github.com/passepartoutvpn/passepartout-apple/issues/181), [tunnelkit#300](https://github.com/passepartoutvpn/tunnelkit/pull/300) - -## 2.0.1 (2022-10-17) - -### Added - -- IVPN provider. -- OpenVPN: Support for `--route-nopull`. [#230](https://github.com/passepartoutvpn/passepartout-apple/pull/230) -- App log in Diagnostics screen. [#234](https://github.com/passepartoutvpn/passepartout-apple/pull/234) - -### Changed - -- Retain whitespaces in imported file names. - -### Fixed - -- Oeck provider is available again to free users. -- Randomic crashes on profile updates. [#229](https://github.com/passepartoutvpn/passepartout-apple/pull/229) -- Mullvad: enforce password to avoid "Auth failed". [#233](https://github.com/passepartoutvpn/passepartout-apple/pull/233) - -## 2.0.0 (2022-10-02) - -### Added - -- WireGuard support. [#201](https://github.com/passepartoutvpn/passepartout-apple/issues/201) -- iCloud support. [#137](https://github.com/passepartoutvpn/passepartout-apple/issues/137) - -### Changed - -- App completely rewritten in SwiftUI. - -### Fixed - -- Files occasionally not selectable in browser. [#215](https://github.com/passepartoutvpn/passepartout-apple/issues/215) diff --git a/Gemfile b/Gemfile index 2fa099ba..b0d3c3aa 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -gem "fastlane" +gem "fastlane", ">= 2.2.0", :github => "keeshux/fastlane", :branch => "bugfix/build-multiplatform-for-ipa-pkg" gem "dotenv" plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') diff --git a/Gemfile.lock b/Gemfile.lock index 5eef398a..b2f24de1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,74 +1,9 @@ -GEM - remote: https://rubygems.org/ +GIT + remote: https://github.com/keeshux/fastlane.git + revision: d08c54ec635ea28fe797d76a56af8a83548ecc96 + branch: bugfix/build-multiplatform-for-ipa-pkg specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - artifactory (3.0.17) - atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.977.0) - aws-sdk-core (3.207.0) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.9) - jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.92.0) - aws-sdk-core (~> 3, >= 3.207.0) - aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.164.0) - aws-sdk-core (~> 3, >= 3.207.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.5) - aws-sigv4 (1.10.0) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - base64 (0.2.0) - claide (1.1.0) - colored (1.2) - colored2 (3.1.2) - commander (4.6.0) - highline (~> 2.0.0) - declarative (0.0.20) - digest-crc (0.6.5) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.6.20240107) - dotenv (2.8.1) - emoji_regex (3.2.3) - excon (0.111.0) - faraday (1.10.4) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.2) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - fastimage (2.3.1) - fastlane (2.222.0) + fastlane (2.221.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -109,6 +44,83 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.3.0) + aws-partitions (1.975.0) + aws-sdk-core (3.205.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.9) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.91.0) + aws-sdk-core (~> 3, >= 3.205.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.162.0) + aws-sdk-core (~> 3, >= 3.205.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.9.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + bigdecimal (3.1.8) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + csv (3.3.0) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.111.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.3.1) + fastlane-plugin-translate_gpt (0.1.8.2) + loco_strings (~> 0.1.4.1) + ruby-openai (~> 3.7) + fastlane-plugin-versioning (0.6.0) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) @@ -149,22 +161,37 @@ GEM highline (2.0.3) http-cookie (1.0.7) domain_name (~> 0.5) + httparty (0.22.0) + csv + mini_mime (>= 1.0.0) + multi_xml (>= 0.5.2) httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) jwt (2.9.0) base64 + loco_strings (0.1.4.1) + nokogiri (~> 1.13, >= 1.13.8) mini_magick (4.13.2) mini_mime (1.1.5) + mini_portile2 (2.8.7) multi_json (1.15.0) + multi_xml (0.7.1) + bigdecimal (~> 3.1) multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) nkf (0.2.0) + nokogiri (1.16.7) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) optparse (0.5.0) os (1.1.4) plist (3.7.1) public_suffix (6.0.1) + racc (1.8.1) rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) @@ -173,6 +200,8 @@ GEM retriable (3.1.2) rexml (3.3.7) rouge (2.0.7) + ruby-openai (3.7.0) + httparty (>= 0.18.1) ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.5) @@ -209,12 +238,13 @@ GEM PLATFORMS arm64-darwin-23 - x86_64-darwin-21 - x86_64-linux + ruby DEPENDENCIES dotenv - fastlane + fastlane (>= 2.2.0)! + fastlane-plugin-translate_gpt + fastlane-plugin-versioning BUNDLED WITH - 2.5.17 + 2.5.14 diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index daf11d75..e097a0b9 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -3,301 +3,42 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ - 0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E021D9A284E68580077EF5D /* CoreContext.swift */; }; - 0E021D9D284E68580077EF5D /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E021D9B284E68580077EF5D /* AppContext.swift */; }; - 0E039279281890B100827C10 /* AddHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E039278281890B100827C10 /* AddHostView.swift */; }; - 0E04F0062883462E00BFCE1C /* LightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0052883462E00BFCE1C /* LightUtils.swift */; }; - 0E04F0072883462E00BFCE1C /* LightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0052883462E00BFCE1C /* LightUtils.swift */; }; - 0E04F0092883466500BFCE1C /* DefaultLightUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E04F0082883466500BFCE1C /* DefaultLightUtils.swift */; }; - 0E065F112813269500062CAF /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E065F102813269500062CAF /* WelcomeView.swift */; }; - 0E0838F22872C5FB00A34EC0 /* ItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0838F12872C5FB00A34EC0 /* ItemGroup.swift */; }; - 0E0838F82874147F00A34EC0 /* PassepartoutMenu+StatusButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0838F72874147F00A34EC0 /* PassepartoutMenu+StatusButton.swift */; }; - 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 */; 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 */; }; - 0E0C0729236087A100155AAC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E0C072B236087A100155AAC /* InfoPlist.strings */; }; - 0E0F4C5A29C761850022E884 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C5929C761850022E884 /* SceneDelegate.swift */; }; - 0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */; }; - 0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F4C6329C84B5A0022E884 /* LockableView.swift */; }; - 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 */; }; - 0E2A8D4927ADF87F00207D04 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */; }; - 0E2A8D4F27B04BBA00207D04 /* OrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */; }; - 0E2AC24522EC3AC10037B4B0 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */; }; - 0E2C171B27CB5A3B007E8488 /* GenericCreditsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2C171A27CB5A3A007E8488 /* GenericCreditsView.swift */; }; - 0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2C172A27CB63F9007E8488 /* Reviewer.swift */; }; - 0E2DE71C27DCCFE80067B9E1 /* TunnelKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */; }; - 0E2DE71F27DCD0290067B9E1 /* TunnelKit+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */; }; - 0E2DE72527DCDF550067B9E1 /* WireGuard+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */; }; - 0E2E0B6F2B335A8E00E3204A /* AppPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B6D2B335A8E00E3204A /* AppPreference.swift */; }; - 0E2E0B702B335A8E00E3204A /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B6E2B335A8E00E3204A /* AppError.swift */; }; - 0E2E0B752B335AAB00E3204A /* IntentsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2E0B712B335AAB00E3204A /* IntentsManager.swift */; }; - 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 */; }; - 0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */; }; - 0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */; }; - 0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */; }; - 0E3A593C2A50975700B3FE40 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */; }; - 0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */; }; - 0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; }; - 0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */; }; - 0E3CD47F280DA14B007075C0 /* AddProfileMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD47E280DA14B007075C0 /* AddProfileMenu.swift */; }; - 0E3CD483280DAE92007075C0 /* ProfileView+MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD482280DAE92007075C0 /* ProfileView+MainMenu.swift */; }; - 0E3FC6862867A3F9009B851C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3FC6852867A3F9009B851C /* AppDelegate.swift */; }; - 0E41BD9A286711C3006346B4 /* PassepartoutLauncherApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E41BD99286711C3006346B4 /* PassepartoutLauncherApp.swift */; }; - 0E41BDA72867128A006346B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E41BDA62867128A006346B4 /* AppDelegate.swift */; }; - 0E41BDA928671356006346B4 /* PassepartoutLauncher.app in Embed Launcher */ = {isa = PBXBuildFile; fileRef = 0E41BD97286711C3006346B4 /* PassepartoutLauncher.app */; platformFilter = maccatalyst; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 0E44689627B051C300A14CE4 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689527B051C300A14CE4 /* ProfileView.swift */; }; - 0E44689C27B11B5300A14CE4 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689B27B11B5300A14CE4 /* AboutView.swift */; }; - 0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */; }; - 0E49F6BD27D7639000385834 /* EndpointAdvancedView+WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */; }; - 0E49F6BF27D764AF00385834 /* EndpointAdvancedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */; }; - 0E53249D27D28FC7002565C3 /* Kvitto in Frameworks */ = {isa = PBXBuildFile; productRef = 0E53249C27D28FC7002565C3 /* Kvitto */; }; - 0E5324A927D2AC55002565C3 /* LongContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5324A827D2AC55002565C3 /* LongContentView.swift */; }; - 0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */; }; - 0E5349C627C176C200C71BB3 /* EndpointView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5349C527C176C200C71BB3 /* EndpointView+OpenVPN.swift */; }; - 0E5349C827C176D100C71BB3 /* EndpointView+WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5349C727C176D100C71BB3 /* EndpointView+WireGuard.swift */; }; - 0E5467F02867A4C300F74D1C /* PassepartoutMac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467EE2867A49B00F74D1C /* PassepartoutMac.swift */; }; - 0E5467F32867A54600F74D1C /* MacBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467F22867A54600F74D1C /* MacBundle.swift */; }; - 0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467F62867A57000F74D1C /* MacBridge.swift */; }; - 0E5467F82867A57000F74D1C /* MacBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467F62867A57000F74D1C /* MacBridge.swift */; }; - 0E5467FA2867AA0A00F74D1C /* MacBundleDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467F92867AA0A00F74D1C /* MacBundleDelegate.swift */; }; - 0E5467FE2867AC4900F74D1C /* ProcessTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467FD2867AC4900F74D1C /* ProcessTransformer.m */; }; - 0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467FF2867AC9900F74D1C /* MacUtils.swift */; }; - 0E5468012867AC9A00F74D1C /* MacUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5467FF2867AC9900F74D1C /* MacUtils.swift */; }; - 0E5468042867AD3500F74D1C /* DefaultMacUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5468032867AD3500F74D1C /* DefaultMacUtils.swift */; }; - 0E5468062867AEC500F74D1C /* MacMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5468052867AEC500F74D1C /* MacMenu.swift */; }; - 0E5468072867AEC500F74D1C /* MacMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5468052867AEC500F74D1C /* MacMenu.swift */; }; - 0E5468092867AEF800F74D1C /* DefaultMacMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5468082867AEF800F74D1C /* DefaultMacMenu.swift */; }; - 0E546810286BAF3100F74D1C /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54680F286BAF3100F74D1C /* Item.swift */; }; - 0E546814286BAF3D00F74D1C /* TextItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54680D286B676E00F74D1C /* TextItem.swift */; }; - 0E54681F286CA53C00F74D1C /* SeparatorItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54681E286CA53C00F74D1C /* SeparatorItem.swift */; }; - 0E5683B927C2825D00EAF1CD /* DiagnosticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E5683B827C2825D00EAF1CD /* DiagnosticsView.swift */; }; - 0E6059CB27FCC5DE003F4063 /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E6059C827FCC5DD003F4063 /* Flags.xcassets */; }; - 0E6059CC27FCC5DE003F4063 /* Providers.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E6059C927FCC5DE003F4063 /* Providers.xcassets */; }; - 0E6059CD27FCC5DE003F4063 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E6059CA27FCC5DE003F4063 /* Assets.xcassets */; }; - 0E70589B28377DC40075D1D2 /* VPNStatusText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E70589A28377DC30075D1D2 /* VPNStatusText.swift */; }; - 0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACDC27C0295B00F85C4B /* View+Extensions.swift */; }; - 0E71ACE327C0F2E400F85C4B /* Providers+L10n.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACE227C0F2E300F85C4B /* Providers+L10n.swift */; }; - 0E71ACE927C1055300F85C4B /* NetworkSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACE827C1055200F85C4B /* NetworkSettingsView.swift */; }; - 0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACEA27C1060D00F85C4B /* EndpointView.swift */; }; - 0E71ACEF27C106B500F85C4B /* ProviderPresetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACEE27C106B400F85C4B /* ProviderPresetView.swift */; }; - 0E71ACF127C1073800F85C4B /* ProviderLocationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACF027C1073800F85C4B /* ProviderLocationView.swift */; }; - 0E71ACF727C107CA00F85C4B /* DebugLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACF627C107C900F85C4B /* DebugLogView.swift */; }; - 0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACF827C12E4800F85C4B /* CreditsView.swift */; }; - 0E71ACFB27C12E5300F85C4B /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFA27C12E5300F85C4B /* VersionView.swift */; }; - 0E71ACFD27C1321A00F85C4B /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */; }; - 0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577DE2817E22C00081CBE /* VPNToggle.swift */; }; - 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 */; }; - 0E96D2DE28704C3B005EFBCF /* TextItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2DD28704C3B005EFBCF /* TextItem+ViewModel.swift */; }; - 0E96D2E028718E44005EFBCF /* VPNItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2DF28718E44005EFBCF /* VPNItemGroup.swift */; }; - 0E96D2E228718E78005EFBCF /* VPNItemGroup+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2E128718E78005EFBCF /* VPNItemGroup+ViewModel.swift */; }; - 0E96D2E628718EF3005EFBCF /* ProfileItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2E528718EF3005EFBCF /* ProfileItemGroup.swift */; }; - 0E96D2EC2871A0AD005EFBCF /* HostProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2EB2871A0AD005EFBCF /* HostProfileItem.swift */; }; - 0E96D2EE2871B68C005EFBCF /* HostProfileItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2ED2871B68C005EFBCF /* HostProfileItem+ViewModel.swift */; }; - 0E96D2F02871C563005EFBCF /* PassepartoutMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2EF2871C563005EFBCF /* PassepartoutMenu.swift */; }; - 0E96D2F22871C5CA005EFBCF /* StaticSystemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2F12871C5CA005EFBCF /* StaticSystemMenu.swift */; }; - 0E96D2F42871CDAD005EFBCF /* VisibilityItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2F32871CDAD005EFBCF /* VisibilityItem.swift */; }; - 0E96D2F62871D1FE005EFBCF /* VisibilityItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2F52871D1FE005EFBCF /* VisibilityItem+ViewModel.swift */; }; - 0E96D2F92871D874005EFBCF /* LightProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2F82871D874005EFBCF /* LightProfileManager.swift */; }; - 0E96D2FC2871D94E005EFBCF /* DefaultLightProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2FB2871D94E005EFBCF /* DefaultLightProfileManager.swift */; }; - 0E96D2FD2871D964005EFBCF /* LightProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D2F82871D874005EFBCF /* LightProfileManager.swift */; }; - 0E96D2FF2871F68B005EFBCF /* SwiftGen+Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075F27EC587900208AD9 /* SwiftGen+Strings.swift */; }; - 0E96D3002871F6C8005EFBCF /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; }; - 0E96D30228720067005EFBCF /* LightVPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D30128720067005EFBCF /* LightVPNManager.swift */; }; - 0E96D30328720067005EFBCF /* LightVPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D30128720067005EFBCF /* LightVPNManager.swift */; }; - 0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D3042872010A005EFBCF /* DefaultLightVPNManager.swift */; }; - 0E96D30B28720ED9005EFBCF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E96D30628720AA1005EFBCF /* Assets.xcassets */; }; - 0E96D30D287212FE005EFBCF /* LaunchOnLoginItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D30C287212FE005EFBCF /* LaunchOnLoginItem.swift */; }; - 0E96D30F28721333005EFBCF /* LaunchOnLoginItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D30E28721333005EFBCF /* LaunchOnLoginItem+ViewModel.swift */; }; - 0E96D31128721855005EFBCF /* Constants+Mac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D31028721855005EFBCF /* Constants+Mac.swift */; }; - 0E96D31428721FC3005EFBCF /* ObservableProcessTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */; }; - 0E9AA978259F756A003FAFF1 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */; }; - 0E9C233327F47E95007D5FC7 /* IntentDispatcher+Activities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */; }; - 0E9C3B6F27FC573E00D0F02E /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */; }; - 0E9E5AEF27B44CF1008C95DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E9E5AE227B44CF1008C95DA /* Localizable.strings */; }; - 0E9ED48127FD9BAE003B2316 /* CopySavingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9ED48027FD9BAE003B2316 /* CopySavingButton.swift */; }; - 0EA1D84728805EAE00F3CA48 /* Flags.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0EA1D84628805EAE00F3CA48 /* Flags.xcassets */; }; - 0EA591162733DDDA0096F796 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0EA591142733DDDA0096F796 /* Intents.intentdefinition */; }; - 0EA9030B287045F70087BC73 /* SystemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA9030A287045F70087BC73 /* SystemMenu.swift */; }; - 0EB17EA727D226B400D473B5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; }; - 0EB17EA927D226C900D473B5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; }; - 0EB17EAA27D226C900D473B5 /* Constants+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA527D2263700D473B5 /* Constants+App.swift */; }; - 0EB17EBA27D2560300D473B5 /* PassepartoutProviders+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */; }; - 0EB2B1482733FB6F007705AB /* PassepartoutOpenVPNTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 0EB3413027C7761A00483410 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB3412F27C7761A00483410 /* Binding+Extensions.swift */; }; - 0EB34BCA27C6A70200B126DA /* OnDemandView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB34BC927C6A70200B126DA /* OnDemandView.swift */; }; - 0EB34BCC27C6F41D00B126DA /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB34BCB27C6F41D00B126DA /* Theme.swift */; }; - 0EB4042C27CA0E8C00378B1A /* Unlocalized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB4042B27CA0E8B00378B1A /* Unlocalized.swift */; }; - 0EB4042E27CA136300378B1A /* AddingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB4042D27CA136200378B1A /* AddingTextField.swift */; }; - 0EB90CC129C25BBD00E64628 /* InteractiveConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB90CC029C25BBD00E64628 /* InteractiveConnectionView.swift */; }; - 0EBC074C27EB673C00208AD9 /* ProfileView+Rename.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC074B27EB673C00208AD9 /* ProfileView+Rename.swift */; }; - 0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075427EBC83800208AD9 /* MailComposerView.swift */; }; - 0EBC075B27EC4FFF00208AD9 /* ReportIssueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075A27EC4FFF00208AD9 /* ReportIssueView.swift */; }; - 0EBC075D27EC529000208AD9 /* DebugLog+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075C27EC529000208AD9 /* DebugLog+Constants.swift */; }; - 0EBC076027EC587900208AD9 /* SwiftGen+Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBC075F27EC587900208AD9 /* SwiftGen+Strings.swift */; }; - 0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */; }; - 0EBF254E2B334C980045C547 /* StoreKitReceiptReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBF254D2B334C980045C547 /* StoreKitReceiptReader.swift */; }; - 0EBF25502B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBF254F2B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift */; }; - 0ECB78E9285F5DE300B0E460 /* PassepartoutMac.bundle in Embed Plugins */ = {isa = PBXBuildFile; fileRef = 0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */; platformFilter = maccatalyst; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 0ECF71EE27B6A99300CDB528 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF71ED27B6A99300CDB528 /* AccountView.swift */; }; - 0ED1A5FD2B2B98CC00A0EA90 /* Constants+Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A5FC2B2B98CC00A0EA90 /* Constants+Tunnel.swift */; }; - 0ED1D6DC27DBA41700983466 /* DiagnosticsView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1D6DB27DBA41700983466 /* DiagnosticsView+OpenVPN.swift */; }; - 0ED1D6DE27DBA42100983466 /* DiagnosticsView+WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1D6DD27DBA42100983466 /* DiagnosticsView+WireGuard.swift */; }; - 0ED2B33927D3C49800FD8EA9 /* OpenVPNAppExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 0ED2B33827D3C49800FD8EA9 /* OpenVPNAppExtension */; }; - 0ED2B34527D3C77800FD8EA9 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; }; - 0ED2B35B27D3C94F00FD8EA9 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2B35A27D3C94F00FD8EA9 /* PacketTunnelProvider.swift */; }; - 0ED2B36027D3C99100FD8EA9 /* PassepartoutWireGuardTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0ED2B34A27D3C77800FD8EA9 /* PassepartoutWireGuardTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 0ED2B36727D3C9A300FD8EA9 /* WireGuardAppExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 0ED2B36627D3C9A300FD8EA9 /* WireGuardAppExtension */; }; - 0ED30DCC27EA197D0057D8A3 /* RevealingSecureField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED30DCB27EA197C0057D8A3 /* RevealingSecureField.swift */; }; - 0ED30DCF27EA1EF80057D8A3 /* PaywallView+Restricted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED30DCE27EA1EF80057D8A3 /* PaywallView+Restricted.swift */; }; - 0ED30DD227EA1F650057D8A3 /* PaywallView+Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED30DD127EA1F650057D8A3 /* PaywallView+Purchase.swift */; }; - 0ED30DDB27EA351C0057D8A3 /* Constants+Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED30DDA27EA351C0057D8A3 /* Constants+Tunnel.swift */; }; - 0ED30DDD27EA35230057D8A3 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; }; - 0ED31C3A20CF39510027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; }; - 0ED7D62F2867328A009F2F8F /* Constants+Library.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED7D62E2867328A009F2F8F /* Constants+Library.swift */; }; - 0ED7D632286733AF009F2F8F /* Constants+Launcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED7D631286733AF009F2F8F /* Constants+Launcher.swift */; }; - 0ED7D633286733D8009F2F8F /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB17EA127D2263700D473B5 /* Constants.swift */; }; - 0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED89C1427DE0A0C008B36D6 /* Shortcut.swift */; }; - 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 */; }; - 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 */; }; - 0EF2212B27E667EA001D0BD7 /* AddProviderView+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF2212A27E667EA001D0BD7 /* AddProviderView+Name.swift */; }; - 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 */; }; - A3A7CC482878DC9F00172D7D /* ProviderServerItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A7CC472878DC9F00172D7D /* ProviderServerItem+ViewModel.swift */; }; - A3A7CC4A28790BD900172D7D /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A7CC4928790BD900172D7D /* Theme.swift */; }; - A3A7CC56287D56E800172D7D /* ProviderLocationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A7CC55287D56E800172D7D /* ProviderLocationItem.swift */; }; - A3A7CC58287D576400172D7D /* ProviderLocationItem+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A7CC57287D576400172D7D /* ProviderLocationItem+ViewModel.swift */; }; - A3D5B04C2A6C6CF2008016D5 /* EndpointView+Add.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3D5B04B2A6C6CF2008016D5 /* EndpointView+Add.swift */; }; + 0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */; }; + 0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */; }; + 0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */; }; + 0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */; }; + 0EBE80DA2BF55C0E00E36A20 /* AppLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 0EBE80D92BF55C0E00E36A20 /* AppLibrary */; }; + 0EBE80DC2BF55C0E00E36A20 /* TunnelLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 0EBE80DB2BF55C0E00E36A20 /* TunnelLibrary */; }; + 0EC066D12C7DC47600D88A94 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EC066D02C7DC47600D88A94 /* LaunchScreen.storyboard */; platformFilter = ios; }; + 0EC332CA2B8A1808000B9C2F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */; }; + 0EC332D22B8A1808000B9C2F /* PassepartoutTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 0EC797422B9378E000C093B7 /* Shared+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797402B9378E000C093B7 /* Shared+App.swift */; }; + 0EC797432B9378E000C093B7 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797412B9378E000C093B7 /* Shared.swift */; }; + 0EC797442B93790600C093B7 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797412B9378E000C093B7 /* Shared.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 0E41BDAA286713F6006346B4 /* PBXContainerItemProxy */ = { + 0EC332D02B8A1808000B9C2F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; + containerPortal = 0E06D1872B87629100176E1D /* Project object */; proxyType = 1; - remoteGlobalIDString = 0E41BD96286711C3006346B4; - remoteInfo = PassepartoutLauncher; - }; - 0EB2B1492733FB6F007705AB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0EDE8DBE20C86910004C739C; - remoteInfo = PassepartoutTunnel; - }; - 0ECB78E6285F5CC400B0E460 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0ECB78D9285F52F700B0E460; - remoteInfo = PassepartoutMac; - }; - 0ECF71FB27B6DA6700CDB528 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0ECF71F327B6D9CD00CDB528; - remoteInfo = PassepartoutWireGuard; - }; - 0ED2B36127D3C99100FD8EA9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0ED2B33E27D3C77800FD8EA9; - remoteInfo = WireGuardTunnel; - }; - 0ED2B36A27D3CAB100FD8EA9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0ECF71F327B6D9CD00CDB528; - remoteInfo = WireGuardGo; - }; - 0EE79B342B2EDB9C00C1220C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0E57F63020C83FC5008323CF /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0EE79B302B2EDB5D00C1220C; - remoteInfo = WireGuardGoTV; + remoteGlobalIDString = 0EC332C72B8A1808000B9C2F; + remoteInfo = Tunnel; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 0E3152B7223F9EF500F61841 /* Embed Plugins */ = { + 0EC332D62B8A1808000B9C2F /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( - 0ECB78E9285F5DE300B0E460 /* PassepartoutMac.bundle in Embed Plugins */, - ); - name = "Embed Plugins"; - runOnlyForDeploymentPostprocessing = 0; - }; - 0E41BDA828671339006346B4 /* Embed Launcher */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = Contents/Library/LoginItems; - dstSubfolderSpec = 1; - files = ( - 0E41BDA928671356006346B4 /* PassepartoutLauncher.app in Embed Launcher */, - ); - name = "Embed Launcher"; - runOnlyForDeploymentPostprocessing = 0; - }; - 0EB2B14B2733FB6F007705AB /* Embed Foundation Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - 0ED2B36027D3C99100FD8EA9 /* PassepartoutWireGuardTunnel.appex in Embed Foundation Extensions */, - 0EB2B1482733FB6F007705AB /* PassepartoutOpenVPNTunnel.appex in Embed Foundation Extensions */, + 0EC332D22B8A1808000B9C2F /* PassepartoutTunnel.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -305,1012 +46,211 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0E021D9A284E68580077EF5D /* CoreContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = ""; }; - 0E021D9B284E68580077EF5D /* AppContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = ""; }; - 0E039278281890B100827C10 /* AddHostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostView.swift; sourceTree = ""; }; - 0E04F0052883462E00BFCE1C /* LightUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightUtils.swift; sourceTree = ""; }; - 0E04F0082883466500BFCE1C /* DefaultLightUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLightUtils.swift; sourceTree = ""; }; - 0E065F102813269500062CAF /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; - 0E0838F12872C5FB00A34EC0 /* ItemGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemGroup.swift; sourceTree = ""; }; - 0E0838F72874147F00A34EC0 /* PassepartoutMenu+StatusButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PassepartoutMenu+StatusButton.swift"; sourceTree = ""; }; - 0E0838F92877325A00A34EC0 /* LightProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightProviderManager.swift; sourceTree = ""; }; - 0E0838FC2877334300A34EC0 /* DefaultLightProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLightProviderManager.swift; sourceTree = ""; }; - 0E09E35C2834172800BE1BAE /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; - 0E0BD27227B2EA2C00583AC5 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; - 0E0BD27527B2EB2200583AC5 /* DonateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonateView.swift; sourceTree = ""; }; - 0E0BD27827B2EBE500583AC5 /* ShortcutsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsView.swift; sourceTree = ""; }; - 0E0C072A236087A100155AAC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 0E0C072C236087C800155AAC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; - 0E0F4C5929C761850022E884 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SceneDelegate+Shortcuts.swift"; sourceTree = ""; }; - 0E0F4C6329C84B5A0022E884 /* LockableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockableView.swift; sourceTree = ""; }; - 0E0F4C6529C84CF60022E884 /* LogoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogoView.swift; sourceTree = ""; }; - 0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Errors+L10n.swift"; sourceTree = ""; }; - 0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticsSection.swift; sourceTree = ""; }; - 0E1C0A52238FFF97009FC087 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; - 0E1DC1BE2B3618EE008B755E /* ProfileView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+TV.swift"; sourceTree = ""; }; - 0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileItem.swift; sourceTree = ""; }; - 0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProviderProfileItem+ViewModel.swift"; sourceTree = ""; }; - 0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; - 0E293856285A73BC002A6E0E /* AppContext+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+Shared.swift"; sourceTree = ""; }; - 0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutApp.swift; sourceTree = ""; }; - 0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizerView.swift; sourceTree = ""; }; - 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; - 0E2C171A27CB5A3A007E8488 /* GenericCreditsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericCreditsView.swift; sourceTree = ""; }; - 0E2C172A27CB63F9007E8488 /* Reviewer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reviewer.swift; sourceTree = ""; }; - 0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKit+Extensions.swift"; sourceTree = ""; }; - 0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKit+L10n.swift"; sourceTree = ""; }; - 0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WireGuard+L10n.swift"; sourceTree = ""; }; - 0E2E0B6D2B335A8E00E3204A /* AppPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppPreference.swift; sourceTree = ""; }; - 0E2E0B6E2B335A8E00E3204A /* AppError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = ""; }; - 0E2E0B712B335AAB00E3204A /* IntentsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentsManager.swift; sourceTree = ""; }; - 0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpgradeManagerStrategy.swift; sourceTree = ""; }; - 0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpgradeManager.swift; sourceTree = ""; }; - 0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistenceManager.swift; sourceTree = ""; }; - 0E330F522B30469700930C7C /* MockProfileRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProfileRepository.swift; sourceTree = ""; }; - 0E330F542B30946600930C7C /* ActiveProfileView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActiveProfileView+TV.swift"; sourceTree = ""; }; - 0E330F562B30952300930C7C /* ProfilesList+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfilesList+TV.swift"; sourceTree = ""; }; - 0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenVPN+L10n.swift"; sourceTree = ""; }; - 0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Core+L10n.swift"; sourceTree = ""; }; - 0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericVersionView.swift; sourceTree = ""; }; - 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+Scene.swift"; sourceTree = ""; }; - 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnDemandView+SSID.swift"; sourceTree = ""; }; - 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileAvailability.swift; sourceTree = ""; }; - 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; - 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddHostView+Name.swift"; sourceTree = ""; }; - 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = ""; }; - 0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Provider.swift"; sourceTree = ""; }; - 0E3CD47E280DA14B007075C0 /* AddProfileMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProfileMenu.swift; sourceTree = ""; }; - 0E3CD482280DAE92007075C0 /* ProfileView+MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+MainMenu.swift"; sourceTree = ""; }; - 0E3FC6842867A3BA009B851C /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; - 0E3FC6852867A3F9009B851C /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 0E41BD97286711C3006346B4 /* PassepartoutLauncher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PassepartoutLauncher.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 0E41BD99286711C3006346B4 /* PassepartoutLauncherApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassepartoutLauncherApp.swift; sourceTree = ""; }; - 0E41BDA2286711C6006346B4 /* Launcher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Launcher.entitlements; sourceTree = ""; }; - 0E41BDA62867128A006346B4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 0E41BDAC286715A7006346B4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 0E44689527B051C300A14CE4 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; - 0E44689B27B11B5300A14CE4 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; - 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+OpenVPN.swift"; sourceTree = ""; }; - 0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+WireGuard.swift"; sourceTree = ""; }; - 0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointAdvancedView.swift; sourceTree = ""; }; - 0E5324A827D2AC55002565C3 /* LongContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongContentView.swift; sourceTree = ""; }; - 0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyledPicker.swift; sourceTree = ""; }; - 0E5349C527C176C200C71BB3 /* EndpointView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointView+OpenVPN.swift"; sourceTree = ""; }; - 0E5349C727C176D100C71BB3 /* EndpointView+WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointView+WireGuard.swift"; sourceTree = ""; }; - 0E5467EE2867A49B00F74D1C /* PassepartoutMac.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutMac.swift; sourceTree = ""; }; - 0E5467F22867A54600F74D1C /* MacBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacBundle.swift; sourceTree = ""; }; - 0E5467F62867A57000F74D1C /* MacBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacBridge.swift; sourceTree = ""; }; - 0E5467F92867AA0A00F74D1C /* MacBundleDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacBundleDelegate.swift; sourceTree = ""; }; - 0E5467FC2867AC4900F74D1C /* ProcessTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessTransformer.h; sourceTree = ""; }; - 0E5467FD2867AC4900F74D1C /* ProcessTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProcessTransformer.m; sourceTree = ""; }; - 0E5467FF2867AC9900F74D1C /* MacUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacUtils.swift; sourceTree = ""; }; - 0E5468032867AD3500F74D1C /* DefaultMacUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultMacUtils.swift; sourceTree = ""; }; - 0E5468052867AEC500F74D1C /* MacMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacMenu.swift; sourceTree = ""; }; - 0E5468082867AEF800F74D1C /* DefaultMacMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultMacMenu.swift; sourceTree = ""; }; - 0E54680D286B676E00F74D1C /* TextItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextItem.swift; sourceTree = ""; }; - 0E54680F286BAF3100F74D1C /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; - 0E54681E286CA53C00F74D1C /* SeparatorItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorItem.swift; sourceTree = ""; }; - 0E5683B827C2825D00EAF1CD /* DiagnosticsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticsView.swift; sourceTree = ""; }; - 0E57F63820C83FC5008323CF /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 0E57F64720C83FC7008323CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 0E6059C827FCC5DD003F4063 /* Flags.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Flags.xcassets; sourceTree = ""; }; - 0E6059C927FCC5DE003F4063 /* Providers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Providers.xcassets; sourceTree = ""; }; - 0E6059CA27FCC5DE003F4063 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 0E70589A28377DC30075D1D2 /* VPNStatusText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNStatusText.swift; sourceTree = ""; }; - 0E71ACDC27C0295B00F85C4B /* View+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = ""; }; - 0E71ACE227C0F2E300F85C4B /* Providers+L10n.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Providers+L10n.swift"; sourceTree = ""; }; - 0E71ACE827C1055200F85C4B /* NetworkSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsView.swift; sourceTree = ""; }; - 0E71ACEA27C1060D00F85C4B /* EndpointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointView.swift; sourceTree = ""; }; - 0E71ACEE27C106B400F85C4B /* ProviderPresetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderPresetView.swift; sourceTree = ""; }; - 0E71ACF027C1073800F85C4B /* ProviderLocationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderLocationView.swift; sourceTree = ""; }; - 0E71ACF627C107C900F85C4B /* DebugLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLogView.swift; sourceTree = ""; }; - 0E71ACF827C12E4800F85C4B /* CreditsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsView.swift; sourceTree = ""; }; - 0E71ACFA27C12E5300F85C4B /* VersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionView.swift; sourceTree = ""; }; - 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = ""; }; - 0E7577DE2817E22C00081CBE /* VPNToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNToggle.swift; sourceTree = ""; }; - 0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+OpenVPN.swift"; sourceTree = ""; }; - 0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Picker+Network.swift"; sourceTree = ""; }; - 0E859B822B2EE08700F80D92 /* OrganizerView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+TV.swift"; sourceTree = ""; }; - 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostViewModel.swift; sourceTree = ""; }; - 0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Configuration.swift"; sourceTree = ""; }; - 0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Extra.swift"; sourceTree = ""; }; - 0E96D2DD28704C3B005EFBCF /* TextItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TextItem+ViewModel.swift"; sourceTree = ""; }; - 0E96D2DF28718E44005EFBCF /* VPNItemGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNItemGroup.swift; sourceTree = ""; }; - 0E96D2E128718E78005EFBCF /* VPNItemGroup+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VPNItemGroup+ViewModel.swift"; sourceTree = ""; }; - 0E96D2E528718EF3005EFBCF /* ProfileItemGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileItemGroup.swift; sourceTree = ""; }; - 0E96D2EB2871A0AD005EFBCF /* HostProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostProfileItem.swift; sourceTree = ""; }; - 0E96D2ED2871B68C005EFBCF /* HostProfileItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HostProfileItem+ViewModel.swift"; sourceTree = ""; }; - 0E96D2EF2871C563005EFBCF /* PassepartoutMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassepartoutMenu.swift; sourceTree = ""; }; - 0E96D2F12871C5CA005EFBCF /* StaticSystemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticSystemMenu.swift; sourceTree = ""; }; - 0E96D2F32871CDAD005EFBCF /* VisibilityItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibilityItem.swift; sourceTree = ""; }; - 0E96D2F52871D1FE005EFBCF /* VisibilityItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibilityItem+ViewModel.swift"; sourceTree = ""; }; - 0E96D2F82871D874005EFBCF /* LightProfileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightProfileManager.swift; sourceTree = ""; }; - 0E96D2FB2871D94E005EFBCF /* DefaultLightProfileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLightProfileManager.swift; sourceTree = ""; }; - 0E96D30128720067005EFBCF /* LightVPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightVPNManager.swift; sourceTree = ""; }; - 0E96D3042872010A005EFBCF /* DefaultLightVPNManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultLightVPNManager.swift; sourceTree = ""; }; - 0E96D30628720AA1005EFBCF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 0E96D30C287212FE005EFBCF /* LaunchOnLoginItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchOnLoginItem.swift; sourceTree = ""; }; - 0E96D30E28721333005EFBCF /* LaunchOnLoginItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LaunchOnLoginItem+ViewModel.swift"; sourceTree = ""; }; - 0E96D31028721855005EFBCF /* Constants+Mac.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+Mac.swift"; sourceTree = ""; }; - 0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableProcessTransformer.swift; sourceTree = ""; }; - 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; - 0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IntentDispatcher+Activities.swift"; sourceTree = ""; }; - 0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; - 0E9E5AE327B44CF1008C95DA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AE427B44CF1008C95DA /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AE527B44CF1008C95DA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; - 0E9E5AE627B44CF1008C95DA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AE727B44CF1008C95DA /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AE827B44CF1008C95DA /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AE927B44CF1008C95DA /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AEA27B44CF1008C95DA /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AEB27B44CF1008C95DA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AEC27B44CF1008C95DA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AED27B44CF1008C95DA /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; - 0E9E5AEE27B44CF1008C95DA /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = ""; }; - 0E9ED48027FD9BAE003B2316 /* CopySavingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopySavingButton.swift; sourceTree = ""; }; - 0EA1D84628805EAE00F3CA48 /* Flags.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Flags.xcassets; sourceTree = ""; }; - 0EA591122733DD4E0096F796 /* IntentDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentDispatcher.swift; sourceTree = ""; }; - 0EA591152733DDDA0096F796 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; - 0EA591182733DDF60096F796 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = ""; }; - 0EA5911A2733DDF80096F796 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Intents.strings"; sourceTree = ""; }; - 0EA5911C2733DDF80096F796 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Intents.strings; sourceTree = ""; }; - 0EA5911E2733DDF90096F796 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Intents.strings; sourceTree = ""; }; - 0EA591202733DDF90096F796 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Intents.strings; sourceTree = ""; }; - 0EA591222733DDFA0096F796 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Intents.strings; sourceTree = ""; }; - 0EA591242733DDFA0096F796 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Intents.strings; sourceTree = ""; }; - 0EA591262733DDFB0096F796 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Intents.strings; sourceTree = ""; }; - 0EA591282733DDFB0096F796 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Intents.strings; sourceTree = ""; }; - 0EA5912A2733DDFC0096F796 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Intents.strings; sourceTree = ""; }; - 0EA5912C2733DDFC0096F796 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Intents.strings; sourceTree = ""; }; - 0EA5912E2733DDFD0096F796 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Intents.strings; sourceTree = ""; }; - 0EA9030A287045F70087BC73 /* SystemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMenu.swift; sourceTree = ""; }; - 0EB17EA127D2263700D473B5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - 0EB17EA527D2263700D473B5 /* Constants+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+App.swift"; sourceTree = ""; }; - 0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PassepartoutProviders+Extensions.swift"; sourceTree = ""; }; - 0EB3412F27C7761A00483410 /* Binding+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = ""; }; - 0EB34BC927C6A70200B126DA /* OnDemandView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDemandView.swift; sourceTree = ""; }; - 0EB34BCB27C6F41D00B126DA /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; - 0EB4042B27CA0E8B00378B1A /* Unlocalized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Unlocalized.swift; sourceTree = ""; }; - 0EB4042D27CA136200378B1A /* AddingTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddingTextField.swift; sourceTree = ""; }; - 0EB90CC029C25BBD00E64628 /* InteractiveConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveConnectionView.swift; sourceTree = ""; }; - 0EBC074B27EB673C00208AD9 /* ProfileView+Rename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Rename.swift"; sourceTree = ""; }; - 0EBC075427EBC83800208AD9 /* MailComposerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MailComposerView.swift; sourceTree = ""; }; - 0EBC075A27EC4FFF00208AD9 /* ReportIssueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportIssueView.swift; sourceTree = ""; }; - 0EBC075C27EC529000208AD9 /* DebugLog+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DebugLog+Constants.swift"; sourceTree = ""; }; - 0EBC075F27EC587900208AD9 /* SwiftGen+Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Strings.swift"; sourceTree = ""; }; - 0EBE2FD02360F88C00F0D5AB /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; - 0EBE2FD12360F88E00F0D5AB /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD22360F88F00F0D5AB /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD32360F89200F0D5AB /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD42360F89300F0D5AB /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD52360F89400F0D5AB /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD62360F89500F0D5AB /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD72360F89600F0D5AB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE2FD82360F89600F0D5AB /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; - 0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+ProfileRow.swift"; sourceTree = ""; }; - 0EBF254D2B334C980045C547 /* StoreKitReceiptReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKitReceiptReader.swift; sourceTree = ""; }; - 0EBF254F2B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultUpgradeManagerStrategy.swift; sourceTree = ""; }; - 0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PassepartoutMac.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - 0ECB78E1285F53ED00B0E460 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 0ECB78EA2861D1F300B0E460 /* PassepartoutLibrary */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PassepartoutLibrary; sourceTree = ""; }; - 0ECF71ED27B6A99300CDB528 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; - 0ED1A5FC2B2B98CC00A0EA90 /* Constants+Tunnel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Constants+Tunnel.swift"; sourceTree = ""; }; - 0ED1D6DB27DBA41700983466 /* DiagnosticsView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiagnosticsView+OpenVPN.swift"; sourceTree = ""; }; - 0ED1D6DD27DBA42100983466 /* DiagnosticsView+WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiagnosticsView+WireGuard.swift"; sourceTree = ""; }; - 0ED2B34A27D3C77800FD8EA9 /* PassepartoutWireGuardTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PassepartoutWireGuardTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 0ED2B35A27D3C94F00FD8EA9 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; - 0ED30DCB27EA197C0057D8A3 /* RevealingSecureField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevealingSecureField.swift; sourceTree = ""; }; - 0ED30DCE27EA1EF80057D8A3 /* PaywallView+Restricted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaywallView+Restricted.swift"; sourceTree = ""; }; - 0ED30DD127EA1F650057D8A3 /* PaywallView+Purchase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaywallView+Purchase.swift"; sourceTree = ""; }; - 0ED30DDA27EA351C0057D8A3 /* Constants+Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+Tunnel.swift"; sourceTree = ""; }; - 0ED31C3920CF39510027975F /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; - 0ED31C3B20CF39510027975F /* Tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = ""; }; - 0ED7D62E2867328A009F2F8F /* Constants+Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Constants+Library.swift"; path = "Passepartout/App/Constants/Constants+Library.swift"; sourceTree = SOURCE_ROOT; }; - 0ED7D631286733AF009F2F8F /* Constants+Launcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Constants+Launcher.swift"; sourceTree = ""; }; - 0ED89C1427DE0A0C008B36D6 /* Shortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcut.swift; sourceTree = ""; }; - 0ED89C1627DE0E05008B36D6 /* IntentEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentEditView.swift; sourceTree = ""; }; - 0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShortcutsView+Add.swift"; sourceTree = ""; }; - 0ED89C1D27DE3F8D008B36D6 /* IntentAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentAddView.swift; sourceTree = ""; }; - 0EDCEF692B337BEB0023A7FF /* PassepartoutLibrary.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PassepartoutLibrary.xctestplan; sourceTree = ""; }; - 0EDE02C127F61C79000FBE3C /* EditableTextList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTextList.swift; sourceTree = ""; }; - 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 = ""; }; - 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 = ""; }; - 0EE11CD1280D8317003BE431 /* SettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsButton.swift; sourceTree = ""; }; - 0EE562772B2EE3EC000C52F6 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; - 0EE79B2E2B2ED99500C1220C /* MainView+TV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainView+TV.swift"; sourceTree = ""; }; - 0EE8B7E227FF340F00B68621 /* VPNProtocolType+FileExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VPNProtocolType+FileExtensions.swift"; sourceTree = ""; }; - 0EF0FAF527DD0211007EB181 /* PaywallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallView.swift; sourceTree = ""; }; - 0EF0FAF827DD212C007EB181 /* IntentActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentActivity.swift; sourceTree = ""; }; - 0EF2212A27E667EA001D0BD7 /* AddProviderView+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddProviderView+Name.swift"; sourceTree = ""; }; - 0EF2212C27E66EB5001D0BD7 /* AddProviderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProviderView.swift; sourceTree = ""; }; - 0EF2212E27E66F60001D0BD7 /* AddProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProfileView.swift; sourceTree = ""; }; - 0EF2213027E674BD001D0BD7 /* AddProviderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProviderViewModel.swift; sourceTree = ""; }; - 0EF6563D2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEPacketTunnelProvider+Expiration.swift"; sourceTree = ""; }; - 0EF656402B36C00E00CEFC96 /* TunnelError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelError.swift; sourceTree = ""; }; - 0EF8C5A728213C510053CE89 /* OrganizerView+Profiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrganizerView+Profiles.swift"; sourceTree = ""; }; - A373484D29DC4F4500D1613C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; - A373484E29DC504000D1613C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; - A373484F29DC52CA00D1613C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Intents.strings; sourceTree = ""; }; - A38D607628AFCFD20005C271 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - A3A7CC452878DC8300172D7D /* ProviderServerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderServerItem.swift; sourceTree = ""; }; - A3A7CC472878DC9F00172D7D /* ProviderServerItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProviderServerItem+ViewModel.swift"; sourceTree = ""; }; - A3A7CC4928790BD900172D7D /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; - A3A7CC55287D56E800172D7D /* ProviderLocationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderLocationItem.swift; sourceTree = ""; }; - A3A7CC57287D576400172D7D /* ProviderLocationItem+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProviderLocationItem+ViewModel.swift"; sourceTree = ""; }; - A3D5B04B2A6C6CF2008016D5 /* EndpointView+Add.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointView+Add.swift"; sourceTree = ""; }; + 0E06D18F2B87629100176E1D /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = ""; }; + 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutApp.swift; sourceTree = ""; }; + 0E7E3D662B9345FD002BBDB4 /* Tunnel.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = ""; }; + 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; + 0E8D852F2C328CA1005493DE /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + 0E94EE5C2B93570600588243 /* Tunnel.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Tunnel.plist; sourceTree = ""; }; + 0EBE80DD2BF55C9100E36A20 /* Library */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Library; sourceTree = ""; }; + 0EC066D02C7DC47600D88A94 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PassepartoutTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 0EC797402B9378E000C093B7 /* Shared+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Shared+App.swift"; sourceTree = ""; }; + 0EC797412B9378E000C093B7 /* Shared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = ""; }; + 0ED1EFDA2C33059600CBD9BD /* App.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = App.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 0E41BD94286711C3006346B4 /* Frameworks */ = { + 0EC332C52B8A1808000B9C2F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0EBE80DC2BF55C0E00E36A20 /* TunnelLibrary in Frameworks */, + 0EC332CA2B8A1808000B9C2F /* NetworkExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 0E57F63520C83FC5008323CF /* Frameworks */ = { + 0ED27CBF2B9331FF0089E26B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0E9C3B6F27FC573E00D0F02E /* CloudKit.framework in Frameworks */, - 0E53249D27D28FC7002565C3 /* Kvitto in Frameworks */, - 0E7A8C0C2A1D4A6100780F4B /* PassepartoutLibrary in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0ECB78D7285F52F700B0E460 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0ED2B34327D3C77800FD8EA9 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0ED2B34527D3C77800FD8EA9 /* NetworkExtension.framework in Frameworks */, - 0ED2B36727D3C9A300FD8EA9 /* WireGuardAppExtension in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0EDE8DBC20C86910004C739C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0ED2B33927D3C49800FD8EA9 /* OpenVPNAppExtension in Frameworks */, - 0ED31C3A20CF39510027975F /* NetworkExtension.framework in Frameworks */, + 0EBE80DA2BF55C0E00E36A20 /* AppLibrary in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0E29385A285A749E002A6E0E /* Context */ = { + 0E06D1862B87629100176E1D = { isa = PBXGroup; children = ( - 0E021D9B284E68580077EF5D /* AppContext.swift */, - 0E293856285A73BC002A6E0E /* AppContext+Shared.swift */, - 0E021D9A284E68580077EF5D /* CoreContext.swift */, - 0E330F522B30469700930C7C /* MockProfileRepository.swift */, - ); - path = Context; - sourceTree = ""; - }; - 0E2C171C27CB6307007E8488 /* Reusable */ = { - isa = PBXGroup; - children = ( - 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */, - 0EB4042D27CA136200378B1A /* AddingTextField.swift */, - 0EB3412F27C7761A00483410 /* Binding+Extensions.swift */, - 0E9ED48027FD9BAE003B2316 /* CopySavingButton.swift */, - 0EDE02C127F61C79000FBE3C /* EditableTextList.swift */, - 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */, - 0E2C171A27CB5A3A007E8488 /* GenericCreditsView.swift */, - 0E34A2CE27CADA6300C73B67 /* GenericVersionView.swift */, - 0EF0FAF827DD212C007EB181 /* IntentActivity.swift */, - 0ED89C1D27DE3F8D008B36D6 /* IntentAddView.swift */, - 0ED89C1627DE0E05008B36D6 /* IntentEditView.swift */, - 0E0F4C6329C84B5A0022E884 /* LockableView.swift */, - 0E5324A827D2AC55002565C3 /* LongContentView.swift */, - 0EBC075427EBC83800208AD9 /* MailComposerView.swift */, - 0ED30DCB27EA197C0057D8A3 /* RevealingSecureField.swift */, - 0E2C172A27CB63F9007E8488 /* Reviewer.swift */, - 0ED89C1427DE0A0C008B36D6 /* Shortcut.swift */, - 0E5349BD27C16A4500C71BB3 /* StyledPicker.swift */, - ); - path = Reusable; - sourceTree = ""; - }; - 0E2E0B792B335B3C00E3204A /* Managers */ = { - isa = PBXGroup; - children = ( - 0EBF254F2B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift */, - 0E2E0B712B335AAB00E3204A /* IntentsManager.swift */, - 0E2E0B742B335AAB00E3204A /* PersistenceManager.swift */, - 0E2E0B732B335AAB00E3204A /* UpgradeManager.swift */, - 0E2E0B722B335AAB00E3204A /* UpgradeManagerStrategy.swift */, - ); - path = Managers; - sourceTree = ""; - }; - 0E34A2B827CAA8EA00C73B67 /* L10n */ = { - isa = PBXGroup; - children = ( - 0E34A2B527CAA8CC00C73B67 /* Core+L10n.swift */, - 0E1AD5CD2A268645002AE6E6 /* Errors+L10n.swift */, - 0E34A2AF27CAA84500C73B67 /* OpenVPN+L10n.swift */, - 0E71ACE227C0F2E300F85C4B /* Providers+L10n.swift */, - 0E2DE71E27DCD0290067B9E1 /* TunnelKit+L10n.swift */, - 0EB4042B27CA0E8B00378B1A /* Unlocalized.swift */, - 0E2DE72427DCDF550067B9E1 /* WireGuard+L10n.swift */, - ); - path = L10n; - sourceTree = ""; - }; - 0E35C0AE280EF8A80071FA35 /* Views */ = { - isa = PBXGroup; - children = ( - 0EE79B2D2B2ED96D00C1220C /* TV */, - 0E44689B27B11B5300A14CE4 /* AboutView.swift */, - 0ECF71ED27B6A99300CDB528 /* AccountView.swift */, - 0E039278281890B100827C10 /* AddHostView.swift */, - 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */, - 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */, - 0E3CD47E280DA14B007075C0 /* AddProfileMenu.swift */, - 0EF2212E27E66F60001D0BD7 /* AddProfileView.swift */, - 0EF2212C27E66EB5001D0BD7 /* AddProviderView.swift */, - 0EF2212A27E667EA001D0BD7 /* AddProviderView+Name.swift */, - 0EF2213027E674BD001D0BD7 /* AddProviderViewModel.swift */, - 0E71ACF827C12E4800F85C4B /* CreditsView.swift */, - 0E71ACF627C107C900F85C4B /* DebugLogView.swift */, - 0E1B5F5B29C506AC00FE7D18 /* DiagnosticsSection.swift */, - 0E5683B827C2825D00EAF1CD /* DiagnosticsView.swift */, - 0ED1D6DB27DBA41700983466 /* DiagnosticsView+OpenVPN.swift */, - 0ED1D6DD27DBA42100983466 /* DiagnosticsView+WireGuard.swift */, - 0E0BD27527B2EB2200583AC5 /* DonateView.swift */, - 0E49F6BE27D764AF00385834 /* EndpointAdvancedView.swift */, - 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */, - 0E49F6BC27D7639000385834 /* EndpointAdvancedView+WireGuard.swift */, - 0E71ACEA27C1060D00F85C4B /* EndpointView.swift */, - A3D5B04B2A6C6CF2008016D5 /* EndpointView+Add.swift */, - 0E5349C527C176C200C71BB3 /* EndpointView+OpenVPN.swift */, - 0E5349C727C176D100C71BB3 /* EndpointView+WireGuard.swift */, - 0EB90CC029C25BBD00E64628 /* InteractiveConnectionView.swift */, - 0E0F4C6529C84CF60022E884 /* LogoView.swift */, - 0E0BD27227B2EA2C00583AC5 /* MainView.swift */, - 0E71ACE827C1055200F85C4B /* NetworkSettingsView.swift */, - 0EB34BC927C6A70200B126DA /* OnDemandView.swift */, - 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */, - 0E2A8D4E27B04BB900207D04 /* OrganizerView.swift */, - 0EBE880E281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift */, - 0EF8C5A728213C510053CE89 /* OrganizerView+Profiles.swift */, - 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */, - 0EF0FAF527DD0211007EB181 /* PaywallView.swift */, - 0ED30DD127EA1F650057D8A3 /* PaywallView+Purchase.swift */, - 0ED30DCE27EA1EF80057D8A3 /* PaywallView+Restricted.swift */, - 0E44689527B051C300A14CE4 /* ProfileView.swift */, - 0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */, - 0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */, - 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 */, - 0EBC075A27EC4FFF00208AD9 /* ReportIssueView.swift */, - 0EE11CD1280D8317003BE431 /* SettingsButton.swift */, - A38D607628AFCFD20005C271 /* SettingsView.swift */, - 0E0BD27827B2EBE500583AC5 /* ShortcutsView.swift */, - 0ED89C1B27DE3ABC008B36D6 /* ShortcutsView+Add.swift */, - 0E71ACFA27C12E5300F85C4B /* VersionView.swift */, - 0E71ACDC27C0295B00F85C4B /* View+Extensions.swift */, - 0E70589A28377DC30075D1D2 /* VPNStatusText.swift */, - 0E7577DE2817E22C00081CBE /* VPNToggle.swift */, - 0E065F102813269500062CAF /* WelcomeView.swift */, - ); - path = Views; - sourceTree = ""; - }; - 0E3A59402A54AD4300B3FE40 /* Domain */ = { - isa = PBXGroup; - children = ( - 0E2E0B6E2B335A8E00E3204A /* AppError.swift */, - 0E2E0B6D2B335A8E00E3204A /* AppPreference.swift */, - 0EA591122733DD4E0096F796 /* IntentDispatcher.swift */, - 0E9C233227F47E95007D5FC7 /* IntentDispatcher+Activities.swift */, - 0EA591142733DDDA0096F796 /* Intents.intentdefinition */, - 0E7A8C082A1D40BA00780F4B /* Picker+Network.swift */, - 0E7A8C072A1D40BA00780F4B /* Picker+OpenVPN.swift */, - 0EBF254D2B334C980045C547 /* StoreKitReceiptReader.swift */, - ); - path = Domain; - sourceTree = ""; - }; - 0E41BD98286711C3006346B4 /* Launcher */ = { - isa = PBXGroup; - children = ( - 0ED7D630286733A1009F2F8F /* Constants */, - 0E41BDA2286711C6006346B4 /* Launcher.entitlements */, - 0E41BDAC286715A7006346B4 /* Info.plist */, - 0E41BDA62867128A006346B4 /* AppDelegate.swift */, - 0E41BD99286711C3006346B4 /* PassepartoutLauncherApp.swift */, - ); - path = Launcher; - sourceTree = ""; - }; - 0E49F6C927DB398100385834 /* Extensions */ = { - isa = PBXGroup; - children = ( - 0EBC075C27EC529000208AD9 /* DebugLog+Constants.swift */, - 0EB17EB927D2560300D473B5 /* PassepartoutProviders+Extensions.swift */, - 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */, - 0E2DE71B27DCCFE80067B9E1 /* TunnelKit+Extensions.swift */, - 0EE8B7E227FF340F00B68621 /* VPNProtocolType+FileExtensions.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - 0E5467F12867A52B00F74D1C /* Mac */ = { - isa = PBXGroup; - children = ( - 0E96D2FA2871D93B005EFBCF /* Managers */, - 0E5467F92867AA0A00F74D1C /* MacBundleDelegate.swift */, - 0E5467F22867A54600F74D1C /* MacBundle.swift */, - ); - path = Mac; - sourceTree = ""; - }; - 0E5467F52867A55F00F74D1C /* Mac */ = { - isa = PBXGroup; - children = ( - 0E96D2F72871D868005EFBCF /* Managers */, - 0E5467F62867A57000F74D1C /* MacBridge.swift */, - 0E5468052867AEC500F74D1C /* MacMenu.swift */, - 0E5467FF2867AC9900F74D1C /* MacUtils.swift */, - ); - path = Mac; - sourceTree = ""; - }; - 0E5467FB2867AC4900F74D1C /* Reusable */ = { - isa = PBXGroup; - children = ( - 0E54680F286BAF3100F74D1C /* Item.swift */, - 0E0838F12872C5FB00A34EC0 /* ItemGroup.swift */, - 0E96D31328721FC3005EFBCF /* ObservableProcessTransformer.swift */, - 0E54681E286CA53C00F74D1C /* SeparatorItem.swift */, - 0E96D2F12871C5CA005EFBCF /* StaticSystemMenu.swift */, - 0EA9030A287045F70087BC73 /* SystemMenu.swift */, - 0E54680D286B676E00F74D1C /* TextItem.swift */, - 0E96D2DD28704C3B005EFBCF /* TextItem+ViewModel.swift */, - 0E5467FC2867AC4900F74D1C /* ProcessTransformer.h */, - 0E5467FD2867AC4900F74D1C /* ProcessTransformer.m */, - ); - path = Reusable; - sourceTree = ""; - }; - 0E5468022867AD2100F74D1C /* Mac */ = { - isa = PBXGroup; - children = ( - 0E5468082867AEF800F74D1C /* DefaultMacMenu.swift */, - 0E5468032867AD3500F74D1C /* DefaultMacUtils.swift */, - ); - path = Mac; - sourceTree = ""; - }; - 0E54680C286B675F00F74D1C /* Views */ = { - isa = PBXGroup; - children = ( - 0E96D2EB2871A0AD005EFBCF /* HostProfileItem.swift */, - 0E96D2ED2871B68C005EFBCF /* HostProfileItem+ViewModel.swift */, - 0E96D30C287212FE005EFBCF /* LaunchOnLoginItem.swift */, - 0E96D30E28721333005EFBCF /* LaunchOnLoginItem+ViewModel.swift */, - 0E96D2EF2871C563005EFBCF /* PassepartoutMenu.swift */, - 0E0838F72874147F00A34EC0 /* PassepartoutMenu+StatusButton.swift */, - 0E96D2E528718EF3005EFBCF /* ProfileItemGroup.swift */, - A3A7CC55287D56E800172D7D /* ProviderLocationItem.swift */, - A3A7CC57287D576400172D7D /* ProviderLocationItem+ViewModel.swift */, - 0E1F5627287F0ECB00F8ADD7 /* ProviderProfileItem.swift */, - 0E1F5629287F0EEE00F8ADD7 /* ProviderProfileItem+ViewModel.swift */, - A3A7CC452878DC8300172D7D /* ProviderServerItem.swift */, - A3A7CC472878DC9F00172D7D /* ProviderServerItem+ViewModel.swift */, - 0E96D2F32871CDAD005EFBCF /* VisibilityItem.swift */, - 0E96D2F52871D1FE005EFBCF /* VisibilityItem+ViewModel.swift */, - 0E96D2DF28718E44005EFBCF /* VPNItemGroup.swift */, - 0E96D2E128718E78005EFBCF /* VPNItemGroup+ViewModel.swift */, - ); - path = Views; - sourceTree = ""; - }; - 0E57F62F20C83FC5008323CF = { - isa = PBXGroup; - children = ( - 0EE315DB2733104700F5D461 /* Packages */, - 0E23B4A12298559800304C30 /* Config.xcconfig */, - 0EDCEF692B337BEB0023A7FF /* PassepartoutLibrary.xctestplan */, - 0E9AA982259F7674003FAFF1 /* Passepartout */, - 0E57F63920C83FC5008323CF /* Products */, - 374B9F085E1148C37CF9117A /* Frameworks */, + 0E7E3D592B9345FD002BBDB4 /* Passepartout */, + 0E06D1902B87629100176E1D /* Products */, + 0E06D1A32B8762AD00176E1D /* Frameworks */, ); sourceTree = ""; }; - 0E57F63920C83FC5008323CF /* Products */ = { + 0E06D1902B87629100176E1D /* Products */ = { isa = PBXGroup; children = ( - 0E57F63820C83FC5008323CF /* Passepartout.app */, - 0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */, - 0ED2B34A27D3C77800FD8EA9 /* PassepartoutWireGuardTunnel.appex */, - 0E41BD97286711C3006346B4 /* PassepartoutLauncher.app */, - 0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */, + 0E06D18F2B87629100176E1D /* Passepartout.app */, + 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */, ); name = Products; sourceTree = ""; }; - 0E96D2F72871D868005EFBCF /* Managers */ = { + 0E06D1A32B8762AD00176E1D /* Frameworks */ = { isa = PBXGroup; children = ( - 0E96D2F82871D874005EFBCF /* LightProfileManager.swift */, - 0E0838F92877325A00A34EC0 /* LightProviderManager.swift */, - 0E04F0052883462E00BFCE1C /* LightUtils.swift */, - 0E96D30128720067005EFBCF /* LightVPNManager.swift */, - ); - path = Managers; - sourceTree = ""; - }; - 0E96D2FA2871D93B005EFBCF /* Managers */ = { - isa = PBXGroup; - children = ( - 0E96D2FB2871D94E005EFBCF /* DefaultLightProfileManager.swift */, - 0E0838FC2877334300A34EC0 /* DefaultLightProviderManager.swift */, - 0E04F0082883466500BFCE1C /* DefaultLightUtils.swift */, - 0E96D3042872010A005EFBCF /* DefaultLightVPNManager.swift */, - ); - path = Managers; - sourceTree = ""; - }; - 0E96D3122872185E005EFBCF /* Constants */ = { - isa = PBXGroup; - children = ( - 0E96D31028721855005EFBCF /* Constants+Mac.swift */, - A3A7CC4928790BD900172D7D /* Theme.swift */, - ); - path = Constants; - sourceTree = ""; - }; - 0E9AA982259F7674003FAFF1 /* Passepartout */ = { - isa = PBXGroup; - children = ( - 0E9AA983259F76C5003FAFF1 /* App */, - 0ECB78D1285F4F4000B0E460 /* AppShared */, - 0E41BD98286711C3006346B4 /* Launcher */, - 0ECB78DE285F536B00B0E460 /* Mac */, - 0EDE8DC020C86910004C739C /* Tunnel */, - 0EB17EA127D2263700D473B5 /* Constants.swift */, - ); - path = Passepartout; - sourceTree = ""; - }; - 0E9AA983259F76C5003FAFF1 /* App */ = { - isa = PBXGroup; - children = ( - 0ECB78D4285F505D00B0E460 /* Constants */, - 0E29385A285A749E002A6E0E /* Context */, - 0E3A59402A54AD4300B3FE40 /* Domain */, - 0E49F6C927DB398100385834 /* Extensions */, - 0E34A2B827CAA8EA00C73B67 /* L10n */, - 0E5467F12867A52B00F74D1C /* Mac */, - 0E2E0B792B335B3C00E3204A /* Managers */, - 0E2C171C27CB6307007E8488 /* Reusable */, - 0E35C0AE280EF8A80071FA35 /* Views */, - 0E6059CA27FCC5DE003F4063 /* Assets.xcassets */, - 0E6059C827FCC5DD003F4063 /* Flags.xcassets */, - 0E6059C927FCC5DE003F4063 /* Providers.xcassets */, - 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */, - 0EDE8DE220C86A13004C739C /* App.entitlements */, - 0E09E35C2834172800BE1BAE /* Credits.rtf */, - 0E57F64720C83FC7008323CF /* Info.plist */, - 0E0C072B236087A100155AAC /* InfoPlist.strings */, - 0E9E5AE227B44CF1008C95DA /* Localizable.strings */, - 0EE562772B2EE3EC000C52F6 /* LaunchScreen.storyboard */, - 0E3FC6852867A3F9009B851C /* AppDelegate.swift */, - 0E2A8D4727ADF87F00207D04 /* PassepartoutApp.swift */, - 0E0F4C5929C761850022E884 /* SceneDelegate.swift */, - 0E0F4C5B29C76B790022E884 /* SceneDelegate+Shortcuts.swift */, - ); - path = App; - sourceTree = ""; - }; - 0ECB78D1285F4F4000B0E460 /* AppShared */ = { - isa = PBXGroup; - children = ( - 0ECB78D3285F4F9600B0E460 /* Constants */, - 0E5467F52867A55F00F74D1C /* Mac */, - ); - path = AppShared; - sourceTree = ""; - }; - 0ECB78D3285F4F9600B0E460 /* Constants */ = { - isa = PBXGroup; - children = ( - 0EBC075F27EC587900208AD9 /* SwiftGen+Strings.swift */, - ); - path = Constants; - sourceTree = ""; - }; - 0ECB78D4285F505D00B0E460 /* Constants */ = { - isa = PBXGroup; - children = ( - 0EB17EA527D2263700D473B5 /* Constants+App.swift */, - 0ED7D62E2867328A009F2F8F /* Constants+Library.swift */, - 0EB34BCB27C6F41D00B126DA /* Theme.swift */, - ); - path = Constants; - sourceTree = ""; - }; - 0ECB78DE285F536B00B0E460 /* Mac */ = { - isa = PBXGroup; - children = ( - 0E96D3122872185E005EFBCF /* Constants */, - 0E5468022867AD2100F74D1C /* Mac */, - 0E5467FB2867AC4900F74D1C /* Reusable */, - 0E54680C286B675F00F74D1C /* Views */, - 0E96D30628720AA1005EFBCF /* Assets.xcassets */, - 0EA1D84628805EAE00F3CA48 /* Flags.xcassets */, - 0ECB78E1285F53ED00B0E460 /* Info.plist */, - 0E5467EE2867A49B00F74D1C /* PassepartoutMac.swift */, - 0E3FC6842867A3BA009B851C /* Bridging-Header.h */, - ); - path = Mac; - sourceTree = ""; - }; - 0ED2B33C27D3C52900FD8EA9 /* OpenVPN */ = { - isa = PBXGroup; - children = ( - 0ED30DDA27EA351C0057D8A3 /* Constants+Tunnel.swift */, - 0E9AA977259F756A003FAFF1 /* PacketTunnelProvider.swift */, - ); - path = OpenVPN; - sourceTree = ""; - }; - 0ED2B33D27D3C53400FD8EA9 /* WireGuard */ = { - isa = PBXGroup; - children = ( - 0ED1A5FC2B2B98CC00A0EA90 /* Constants+Tunnel.swift */, - 0ED2B35A27D3C94F00FD8EA9 /* PacketTunnelProvider.swift */, - ); - path = WireGuard; - sourceTree = ""; - }; - 0ED7D630286733A1009F2F8F /* Constants */ = { - isa = PBXGroup; - children = ( - 0ED7D631286733AF009F2F8F /* Constants+Launcher.swift */, - ); - path = Constants; - sourceTree = ""; - }; - 0EDE8DC020C86910004C739C /* Tunnel */ = { - isa = PBXGroup; - children = ( - 0ED2B33C27D3C52900FD8EA9 /* OpenVPN */, - 0ED2B33D27D3C53400FD8EA9 /* WireGuard */, - 0ED31C3B20CF39510027975F /* Tunnel.entitlements */, - 0EDE8DC320C86910004C739C /* Info.plist */, - 0EF6563D2B36BFCD00CEFC96 /* NEPacketTunnelProvider+Expiration.swift */, - 0EF656402B36C00E00CEFC96 /* TunnelError.swift */, - ); - path = Tunnel; - sourceTree = ""; - }; - 0EE315DB2733104700F5D461 /* Packages */ = { - isa = PBXGroup; - children = ( - 0ECB78EA2861D1F300B0E460 /* PassepartoutLibrary */, - ); - name = Packages; - sourceTree = ""; - }; - 0EE79B2D2B2ED96D00C1220C /* TV */ = { - isa = PBXGroup; - children = ( - 0E330F542B30946600930C7C /* ActiveProfileView+TV.swift */, - 0EE79B2E2B2ED99500C1220C /* MainView+TV.swift */, - 0E859B822B2EE08700F80D92 /* OrganizerView+TV.swift */, - 0E330F562B30952300930C7C /* ProfilesList+TV.swift */, - ); - path = TV; - sourceTree = ""; - }; - 374B9F085E1148C37CF9117A /* Frameworks */ = { - isa = PBXGroup; - children = ( - 0E9C3B6E27FC573E00D0F02E /* CloudKit.framework */, - 0ED31C3920CF39510027975F /* NetworkExtension.framework */, - 0EDE8DD220C86978004C739C /* NotificationCenter.framework */, + 0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */, ); name = Frameworks; sourceTree = ""; }; + 0E7E3D592B9345FD002BBDB4 /* Passepartout */ = { + isa = PBXGroup; + children = ( + 0E8D852F2C328CA1005493DE /* Config.xcconfig */, + 0E7E3D5A2B9345FD002BBDB4 /* App */, + 0E7E3D612B9345FD002BBDB4 /* Shared */, + 0E7E3D652B9345FD002BBDB4 /* Tunnel */, + 0EBE80DD2BF55C9100E36A20 /* Library */, + ); + path = Passepartout; + sourceTree = ""; + }; + 0E7E3D5A2B9345FD002BBDB4 /* App */ = { + isa = PBXGroup; + children = ( + 0ED1EFDA2C33059600CBD9BD /* App.plist */, + 0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */, + 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */, + 0EC066D02C7DC47600D88A94 /* LaunchScreen.storyboard */, + 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */, + 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */, + ); + path = App; + sourceTree = ""; + }; + 0E7E3D612B9345FD002BBDB4 /* Shared */ = { + isa = PBXGroup; + children = ( + 0EC797412B9378E000C093B7 /* Shared.swift */, + 0EC797402B9378E000C093B7 /* Shared+App.swift */, + ); + path = Shared; + sourceTree = ""; + }; + 0E7E3D652B9345FD002BBDB4 /* Tunnel */ = { + isa = PBXGroup; + children = ( + 0E94EE5C2B93570600588243 /* Tunnel.plist */, + 0E7E3D662B9345FD002BBDB4 /* Tunnel.entitlements */, + 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */, + ); + path = Tunnel; + sourceTree = ""; + }; /* End PBXGroup section */ -/* Begin PBXLegacyTarget section */ - 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */ = { - isa = PBXLegacyTarget; - buildArgumentsString = "$(ACTION)"; - buildConfigurationList = 0ECF71F427B6D9CD00CDB528 /* Build configuration list for PBXLegacyTarget "WireGuardGo" */; - buildPhases = ( - ); - buildToolPath = "$(PROJECT_DIR)/Passepartout/App/Scripts/build_wireguard_go_bridge.sh"; - buildWorkingDirectory = ""; - dependencies = ( - ); - name = WireGuardGo; - 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 */ - 0E41BD96286711C3006346B4 /* PassepartoutLauncher */ = { + 0E06D18E2B87629100176E1D /* Passepartout */ = { isa = PBXNativeTarget; - buildConfigurationList = 0E41BDA3286711C6006346B4 /* Build configuration list for PBXNativeTarget "PassepartoutLauncher" */; + buildConfigurationList = 0E06D19E2B87629200176E1D /* Build configuration list for PBXNativeTarget "Passepartout" */; buildPhases = ( - 0E41BD93286711C3006346B4 /* Sources */, - 0E41BD94286711C3006346B4 /* Frameworks */, - 0E41BD95286711C3006346B4 /* Resources */, + 0E06D18B2B87629100176E1D /* Sources */, + 0ED27CBF2B9331FF0089E26B /* Frameworks */, + 0E06D18D2B87629100176E1D /* Resources */, + 0EC332D62B8A1808000B9C2F /* Embed Foundation Extensions */, + 0E8D852E2C328C54005493DE /* SwiftLint */, ); buildRules = ( ); dependencies = ( - ); - name = PassepartoutLauncher; - productName = PassepartoutLauncher; - productReference = 0E41BD97286711C3006346B4 /* PassepartoutLauncher.app */; - productType = "com.apple.product-type.application"; - }; - 0E57F63720C83FC5008323CF /* Passepartout */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0E57F65520C83FC7008323CF /* Build configuration list for PBXNativeTarget "Passepartout" */; - buildPhases = ( - 0EADDC7227F0677F0093E303 /* Copy Core Data codegen */, - 0E57F63420C83FC5008323CF /* Sources */, - 0E57F63520C83FC5008323CF /* Frameworks */, - 0E57F63620C83FC5008323CF /* Resources */, - 0E41BDA828671339006346B4 /* Embed Launcher */, - 0E3152B7223F9EF500F61841 /* Embed Plugins */, - 0EB2B14B2733FB6F007705AB /* Embed Foundation Extensions */, - 0E1B5F5D29C5082700FE7D18 /* SwiftLint */, - ); - buildRules = ( - ); - dependencies = ( - 0ECB78E7285F5CC400B0E460 /* PBXTargetDependency */, - 0E41BDAB286713F6006346B4 /* PBXTargetDependency */, - 0EB2B14A2733FB6F007705AB /* PBXTargetDependency */, - 0ED2B36227D3C99100FD8EA9 /* PBXTargetDependency */, - 0ECF71FC27B6DA6700CDB528 /* PBXTargetDependency */, - 0EE79B352B2EDB9C00C1220C /* PBXTargetDependency */, + 0E6C0A032BF4047100450362 /* PBXTargetDependency */, + 0EC332D12B8A1808000B9C2F /* PBXTargetDependency */, ); name = Passepartout; packageProductDependencies = ( - 0E53249C27D28FC7002565C3 /* Kvitto */, - 0E7A8C0B2A1D4A6100780F4B /* PassepartoutLibrary */, + 0EBE80D92BF55C0E00E36A20 /* AppLibrary */, ); - productName = Passepartout; - productReference = 0E57F63820C83FC5008323CF /* Passepartout.app */; + productName = PassepartoutKit; + productReference = 0E06D18F2B87629100176E1D /* Passepartout.app */; productType = "com.apple.product-type.application"; }; - 0ECB78D9285F52F700B0E460 /* PassepartoutMac */ = { + 0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */ = { isa = PBXNativeTarget; - buildConfigurationList = 0ECB78DD285F52F700B0E460 /* Build configuration list for PBXNativeTarget "PassepartoutMac" */; + buildConfigurationList = 0EC332D32B8A1808000B9C2F /* Build configuration list for PBXNativeTarget "PassepartoutTunnel" */; buildPhases = ( - 0E5467EF2867A4B100F74D1C /* Sources */, - 0ECB78D7285F52F700B0E460 /* Frameworks */, - 0ECB78D8285F52F700B0E460 /* Resources */, + 0EC332C42B8A1808000B9C2F /* Sources */, + 0EC332C52B8A1808000B9C2F /* Frameworks */, + 0EC332C62B8A1808000B9C2F /* Resources */, ); buildRules = ( ); dependencies = ( + 0E6C0A052BF4047600450362 /* PBXTargetDependency */, ); - name = PassepartoutMac; + name = PassepartoutTunnel; packageProductDependencies = ( + 0EBE80DB2BF55C0E00E36A20 /* TunnelLibrary */, ); - productName = PassepartoutMac; - productReference = 0ECB78DA285F52F700B0E460 /* PassepartoutMac.bundle */; - productType = "com.apple.product-type.bundle"; - }; - 0ED2B33E27D3C77800FD8EA9 /* WireGuardTunnel */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0ED2B34727D3C77800FD8EA9 /* Build configuration list for PBXNativeTarget "WireGuardTunnel" */; - buildPhases = ( - 0ED2B34027D3C77800FD8EA9 /* Sources */, - 0ED2B34327D3C77800FD8EA9 /* Frameworks */, - 0ED2B34627D3C77800FD8EA9 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 0ED2B36B27D3CAB100FD8EA9 /* PBXTargetDependency */, - ); - name = WireGuardTunnel; - packageProductDependencies = ( - 0ED2B36627D3C9A300FD8EA9 /* WireGuardAppExtension */, - ); - productName = PassepartoutWireGuardTunnel; - productReference = 0ED2B34A27D3C77800FD8EA9 /* PassepartoutWireGuardTunnel.appex */; - productType = "com.apple.product-type.app-extension"; - }; - 0EDE8DBE20C86910004C739C /* OpenVPNTunnel */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0EDE8DC920C86910004C739C /* Build configuration list for PBXNativeTarget "OpenVPNTunnel" */; - buildPhases = ( - 0EDE8DBB20C86910004C739C /* Sources */, - 0EDE8DBC20C86910004C739C /* Frameworks */, - 0EDE8DBD20C86910004C739C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = OpenVPNTunnel; - packageProductDependencies = ( - 0ED2B33827D3C49800FD8EA9 /* OpenVPNAppExtension */, - ); - productName = PassepartoutOpenVPNTunnel; - productReference = 0EDE8DBF20C86910004C739C /* PassepartoutOpenVPNTunnel.appex */; + productName = Tunnel; + productReference = 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */; productType = "com.apple.product-type.app-extension"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 0E57F63020C83FC5008323CF /* Project object */ = { + 0E06D1872B87629100176E1D /* Project object */ = { isa = PBXProject; attributes = { - BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1510; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = "Davide De Rosa"; + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1520; + LastUpgradeCheck = 1540; TargetAttributes = { - 0E41BD96286711C3006346B4 = { - CreatedOnToolsVersion = 13.4; + 0E06D18E2B87629100176E1D = { + CreatedOnToolsVersion = 15.2; }; - 0E57F63720C83FC5008323CF = { - CreatedOnToolsVersion = 9.4; - LastSwiftMigration = 1020; - SystemCapabilities = { - com.apple.AccessWiFi = { - enabled = 1; - }; - com.apple.ApplicationGroups.iOS = { - enabled = 1; - }; - com.apple.NetworkExtensions.iOS = { - enabled = 1; - }; - com.apple.Siri = { - enabled = 1; - }; - }; - }; - 0ECB78D9285F52F700B0E460 = { - CreatedOnToolsVersion = 13.4; - LastSwiftMigration = 1340; - }; - 0ECF71F327B6D9CD00CDB528 = { - CreatedOnToolsVersion = 13.2; - }; - 0EDE8DBE20C86910004C739C = { - CreatedOnToolsVersion = 10.0; - LastSwiftMigration = 1020; - SystemCapabilities = { - com.apple.ApplicationGroups.iOS = { - enabled = 1; - }; - com.apple.NetworkExtensions.iOS = { - enabled = 1; - }; - }; + 0EC332C72B8A1808000B9C2F = { + CreatedOnToolsVersion = 15.2; }; }; }; - buildConfigurationList = 0E57F63320C83FC5008323CF /* Build configuration list for PBXProject "Passepartout" */; - compatibilityVersion = "Xcode 9.3"; + buildConfigurationList = 0E06D18A2B87629100176E1D /* Build configuration list for PBXProject "Passepartout" */; + compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, - it, - de, - ru, - nl, - el, - sv, - fr, - es, - pt, - "zh-Hans", - pl, - uk, ); - mainGroup = 0E57F62F20C83FC5008323CF; - packageReferences = ( - 0E53249B27D28FC7002565C3 /* XCRemoteSwiftPackageReference "Kvitto" */, - ); - productRefGroup = 0E57F63920C83FC5008323CF /* Products */; + mainGroup = 0E06D1862B87629100176E1D; + productRefGroup = 0E06D1902B87629100176E1D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 0E57F63720C83FC5008323CF /* Passepartout */, - 0ECB78D9285F52F700B0E460 /* PassepartoutMac */, - 0E41BD96286711C3006346B4 /* PassepartoutLauncher */, - 0EDE8DBE20C86910004C739C /* OpenVPNTunnel */, - 0ED2B33E27D3C77800FD8EA9 /* WireGuardTunnel */, - 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */, - 0EE79B302B2EDB5D00C1220C /* WireGuardGoTV */, + 0E06D18E2B87629100176E1D /* Passepartout */, + 0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 0E41BD95286711C3006346B4 /* Resources */ = { + 0E06D18D2B87629100176E1D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */, + 0EC066D12C7DC47600D88A94 /* LaunchScreen.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 0E57F63620C83FC5008323CF /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0E6059CB27FCC5DE003F4063 /* Flags.xcassets in Resources */, - 0EE562782B2EE3EC000C52F6 /* LaunchScreen.storyboard in Resources */, - 0E0C0729236087A100155AAC /* InfoPlist.strings in Resources */, - 0E6059CC27FCC5DE003F4063 /* Providers.xcassets in Resources */, - 0E6059CD27FCC5DE003F4063 /* Assets.xcassets in Resources */, - 0E9E5AEF27B44CF1008C95DA /* Localizable.strings in Resources */, - 0E09E35D2834172800BE1BAE /* Credits.rtf in Resources */, - 0E2AC24522EC3AC10037B4B0 /* Settings.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0ECB78D8285F52F700B0E460 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0E96D30B28720ED9005EFBCF /* Assets.xcassets in Resources */, - 0EA1D84728805EAE00F3CA48 /* Flags.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0ED2B34627D3C77800FD8EA9 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0EDE8DBD20C86910004C739C /* Resources */ = { + 0EC332C62B8A1808000B9C2F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -1320,7 +260,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0E1B5F5D29C5082700FE7D18 /* SwiftLint */ = { + 0E8D852E2C328C54005493DE /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1338,421 +278,59 @@ shellPath = /bin/sh; shellScript = "PATH=$CUSTOM_SCRIPT_PATH\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; - 0EADDC7227F0677F0093E303 /* Copy Core Data codegen */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Copy Core Data codegen"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "Passepartout/App/Scripts/copy_coredata_codegen.sh\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 0E41BD93286711C3006346B4 /* Sources */ = { + 0E06D18B2B87629100176E1D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0ED7D632286733AF009F2F8F /* Constants+Launcher.swift in Sources */, - 0E41BDA72867128A006346B4 /* AppDelegate.swift in Sources */, - 0E41BD9A286711C3006346B4 /* PassepartoutLauncherApp.swift in Sources */, - 0ED7D633286733D8009F2F8F /* Constants.swift in Sources */, + 0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */, + 0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */, + 0EC797422B9378E000C093B7 /* Shared+App.swift in Sources */, + 0EC797432B9378E000C093B7 /* Shared.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 0E5467EF2867A4B100F74D1C /* Sources */ = { + 0EC332C42B8A1808000B9C2F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A3A7CC462878DC8300172D7D /* ProviderServerItem.swift in Sources */, - 0E96D31128721855005EFBCF /* Constants+Mac.swift in Sources */, - 0E96D3002871F6C8005EFBCF /* Constants.swift in Sources */, - 0E96D2E228718E78005EFBCF /* VPNItemGroup+ViewModel.swift in Sources */, - 0E96D2F02871C563005EFBCF /* PassepartoutMenu.swift in Sources */, - 0E96D2E028718E44005EFBCF /* VPNItemGroup.swift in Sources */, - 0E96D2F62871D1FE005EFBCF /* VisibilityItem+ViewModel.swift in Sources */, - 0E96D30F28721333005EFBCF /* LaunchOnLoginItem+ViewModel.swift in Sources */, - 0E5467F82867A57000F74D1C /* MacBridge.swift in Sources */, - 0E5468042867AD3500F74D1C /* DefaultMacUtils.swift in Sources */, - 0E5468092867AEF800F74D1C /* DefaultMacMenu.swift in Sources */, - 0E0838FB2877325A00A34EC0 /* LightProviderManager.swift in Sources */, - 0E1F5628287F0ECB00F8ADD7 /* ProviderProfileItem.swift in Sources */, - A3A7CC482878DC9F00172D7D /* ProviderServerItem+ViewModel.swift in Sources */, - 0E5467FE2867AC4900F74D1C /* ProcessTransformer.m in Sources */, - 0EA9030B287045F70087BC73 /* SystemMenu.swift in Sources */, - 0E96D2DE28704C3B005EFBCF /* TextItem+ViewModel.swift in Sources */, - 0E0838F22872C5FB00A34EC0 /* ItemGroup.swift in Sources */, - A3A7CC4A28790BD900172D7D /* Theme.swift in Sources */, - 0E0838F82874147F00A34EC0 /* PassepartoutMenu+StatusButton.swift in Sources */, - 0E5468012867AC9A00F74D1C /* MacUtils.swift in Sources */, - 0E546810286BAF3100F74D1C /* Item.swift in Sources */, - A3A7CC56287D56E800172D7D /* ProviderLocationItem.swift in Sources */, - 0E96D30328720067005EFBCF /* LightVPNManager.swift in Sources */, - 0E96D2F42871CDAD005EFBCF /* VisibilityItem.swift in Sources */, - 0E1F562B287F0EF100F8ADD7 /* ProviderProfileItem+ViewModel.swift in Sources */, - 0E5468072867AEC500F74D1C /* MacMenu.swift in Sources */, - 0E96D2EC2871A0AD005EFBCF /* HostProfileItem.swift in Sources */, - 0E04F0072883462E00BFCE1C /* LightUtils.swift in Sources */, - 0E96D2F22871C5CA005EFBCF /* StaticSystemMenu.swift in Sources */, - A3A7CC58287D576400172D7D /* ProviderLocationItem+ViewModel.swift in Sources */, - 0E5467F02867A4C300F74D1C /* PassepartoutMac.swift in Sources */, - 0E96D30D287212FE005EFBCF /* LaunchOnLoginItem.swift in Sources */, - 0E96D2F92871D874005EFBCF /* LightProfileManager.swift in Sources */, - 0E54681F286CA53C00F74D1C /* SeparatorItem.swift in Sources */, - 0E96D2E628718EF3005EFBCF /* ProfileItemGroup.swift in Sources */, - 0E96D31428721FC3005EFBCF /* ObservableProcessTransformer.swift in Sources */, - 0E96D2FF2871F68B005EFBCF /* SwiftGen+Strings.swift in Sources */, - 0E546814286BAF3D00F74D1C /* TextItem.swift in Sources */, - 0E96D2EE2871B68C005EFBCF /* HostProfileItem+ViewModel.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0E57F63420C83FC5008323CF /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0EA591162733DDDA0096F796 /* Intents.intentdefinition in Sources */, - 0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */, - 0E0BD27927B2EBE500583AC5 /* ShortcutsView.swift in Sources */, - 0E2E0B762B335AAB00E3204A /* UpgradeManagerStrategy.swift in Sources */, - 0E92D7C627F103300033CB7B /* ProfileView+Configuration.swift in Sources */, - 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 */, - 0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */, - 0EF2212D27E66EB5001D0BD7 /* AddProviderView.swift in Sources */, - 0EBF25502B334CA00045C547 /* DefaultUpgradeManagerStrategy.swift in Sources */, - 0EB90CC129C25BBD00E64628 /* InteractiveConnectionView.swift in Sources */, - 0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */, - 0E04F0092883466500BFCE1C /* DefaultLightUtils.swift in Sources */, - 0E5349C827C176D100C71BB3 /* EndpointView+WireGuard.swift in Sources */, - 0EBC076027EC587900208AD9 /* SwiftGen+Strings.swift in Sources */, - 0E7A8C0F2A1D54DE00780F4B /* Picker+OpenVPN.swift in Sources */, - 0E0F4C5C29C76B790022E884 /* SceneDelegate+Shortcuts.swift in Sources */, - 0E5683B927C2825D00EAF1CD /* DiagnosticsView.swift in Sources */, - 0E3FC6862867A3F9009B851C /* AppDelegate.swift in Sources */, - 0E71ACFD27C1321A00F85C4B /* ActivityView.swift in Sources */, - 0E44689627B051C300A14CE4 /* ProfileView.swift in Sources */, - 0EDE02C227F61C79000FBE3C /* EditableTextList.swift in Sources */, - 0E92D7C927F1042A0033CB7B /* ProfileView+Extra.swift in Sources */, - 0EBC074C27EB673C00208AD9 /* ProfileView+Rename.swift in Sources */, - 0E49F6BD27D7639000385834 /* EndpointAdvancedView+WireGuard.swift in Sources */, - 0EB34BCC27C6F41D00B126DA /* Theme.swift in Sources */, - 0EB17EA927D226C900D473B5 /* Constants.swift in Sources */, - 0E5324A927D2AC55002565C3 /* LongContentView.swift in Sources */, - 0ED89C1C27DE3ABC008B36D6 /* ShortcutsView+Add.swift in Sources */, - 0E34A2CF27CADA6300C73B67 /* GenericVersionView.swift in Sources */, - 0E9C233327F47E95007D5FC7 /* IntentDispatcher+Activities.swift in Sources */, - 0EBC075D27EC529000208AD9 /* DebugLog+Constants.swift in Sources */, - 0E3CD47F280DA14B007075C0 /* AddProfileMenu.swift in Sources */, - 0EB17EAA27D226C900D473B5 /* Constants+App.swift in Sources */, - A3D5B04C2A6C6CF2008016D5 /* EndpointView+Add.swift in Sources */, - 0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */, - 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 */, - 0ECF71EE27B6A99300CDB528 /* AccountView.swift in Sources */, - 0E71ACF727C107CA00F85C4B /* DebugLogView.swift in Sources */, - 0EF0FAF927DD212C007EB181 /* IntentActivity.swift in Sources */, - 0EBC075B27EC4FFF00208AD9 /* ReportIssueView.swift in Sources */, - 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 */, - 0ED7D62F2867328A009F2F8F /* Constants+Library.swift in Sources */, - 0E5467FA2867AA0A00F74D1C /* MacBundleDelegate.swift in Sources */, - 0EBF254E2B334C980045C547 /* StoreKitReceiptReader.swift in Sources */, - 0E0BD27327B2EA2C00583AC5 /* MainView.swift in Sources */, - 0E0F4C5A29C761850022E884 /* SceneDelegate.swift in Sources */, - 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 */, - 0E71ACF127C1073800F85C4B /* ProviderLocationView.swift in Sources */, - 0E2A8D4F27B04BBA00207D04 /* OrganizerView.swift in Sources */, - 0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */, - 0E71ACEF27C106B500F85C4B /* ProviderPresetView.swift in Sources */, - 0E1B5F5C29C506AD00FE7D18 /* DiagnosticsSection.swift in Sources */, - 0EF2212F27E66F60001D0BD7 /* AddProfileView.swift in Sources */, - 0E96D2FC2871D94E005EFBCF /* DefaultLightProfileManager.swift in Sources */, - 0EF0FAF627DD0211007EB181 /* PaywallView.swift in Sources */, - 0E7A8C102A1D54DE00780F4B /* Picker+Network.swift in Sources */, - 0E2E0B702B335A8E00E3204A /* AppError.swift in Sources */, - 0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */, - 0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */, - 0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */, - 0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */, - A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */, - 0E34A2B627CAA8CC00C73B67 /* Core+L10n.swift in Sources */, - 0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */, - 0E5467F32867A54600F74D1C /* MacBundle.swift in Sources */, - 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 */, - 0E039279281890B100827C10 /* AddHostView.swift in Sources */, - 0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */, - 0E9ED48127FD9BAE003B2316 /* CopySavingButton.swift in Sources */, - 0E04F0062883462E00BFCE1C /* LightUtils.swift in Sources */, - 0E44689C27B11B5300A14CE4 /* AboutView.swift in Sources */, - 0EE11CD2280D8317003BE431 /* SettingsButton.swift in Sources */, - 0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */, - 0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */, - 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 */, - 0EB4042C27CA0E8C00378B1A /* Unlocalized.swift in Sources */, - 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 */, - 0E96D2FD2871D964005EFBCF /* LightProfileManager.swift in Sources */, - 0E0BD27627B2EB2200583AC5 /* DonateView.swift in Sources */, - 0E2C171B27CB5A3B007E8488 /* GenericCreditsView.swift in Sources */, - 0ED30DD227EA1F650057D8A3 /* PaywallView+Purchase.swift in Sources */, - 0EB3413027C7761A00483410 /* Binding+Extensions.swift in Sources */, - 0E021D9D284E68580077EF5D /* AppContext.swift in Sources */, - 0E2DE72527DCDF550067B9E1 /* WireGuard+L10n.swift in Sources */, - 0E71ACFB27C12E5300F85C4B /* VersionView.swift in Sources */, - 0E1AD5CE2A268645002AE6E6 /* Errors+L10n.swift in Sources */, - 0ED1D6DC27DBA41700983466 /* DiagnosticsView+OpenVPN.swift in Sources */, - 0ED30DCC27EA197D0057D8A3 /* RevealingSecureField.swift in Sources */, - 0E5349C627C176C200C71BB3 /* EndpointView+OpenVPN.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 0ED2B34027D3C77800FD8EA9 /* Sources */ = { - 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; - }; - 0EDE8DBB20C86910004C739C /* Sources */ = { - 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 */, + 0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */, + 0EC797442B93790600C093B7 /* Shared.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 0E41BDAB286713F6006346B4 /* PBXTargetDependency */ = { + 0E6C0A032BF4047100450362 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilter = maccatalyst; - target = 0E41BD96286711C3006346B4 /* PassepartoutLauncher */; - targetProxy = 0E41BDAA286713F6006346B4 /* PBXContainerItemProxy */; + productRef = 0E6C0A022BF4047100450362 /* AppLibrary */; }; - 0EB2B14A2733FB6F007705AB /* PBXTargetDependency */ = { + 0E6C0A052BF4047600450362 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 0EDE8DBE20C86910004C739C /* OpenVPNTunnel */; - targetProxy = 0EB2B1492733FB6F007705AB /* PBXContainerItemProxy */; + productRef = 0E6C0A042BF4047600450362 /* TunnelLibrary */; }; - 0ECB78E7285F5CC400B0E460 /* PBXTargetDependency */ = { + 0EC332D12B8A1808000B9C2F /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilter = maccatalyst; - target = 0ECB78D9285F52F700B0E460 /* PassepartoutMac */; - targetProxy = 0ECB78E6285F5CC400B0E460 /* PBXContainerItemProxy */; - }; - 0ECF71FC27B6DA6700CDB528 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - platformFilters = ( - ios, - maccatalyst, - ); - target = 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */; - targetProxy = 0ECF71FB27B6DA6700CDB528 /* PBXContainerItemProxy */; - }; - 0ED2B36227D3C99100FD8EA9 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0ED2B33E27D3C77800FD8EA9 /* WireGuardTunnel */; - targetProxy = 0ED2B36127D3C99100FD8EA9 /* PBXContainerItemProxy */; - }; - 0ED2B36B27D3CAB100FD8EA9 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0ECF71F327B6D9CD00CDB528 /* WireGuardGo */; - targetProxy = 0ED2B36A27D3CAB100FD8EA9 /* PBXContainerItemProxy */; - }; - 0EE79B352B2EDB9C00C1220C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - platformFilters = ( - tvos, - ); - target = 0EE79B302B2EDB5D00C1220C /* WireGuardGoTV */; - targetProxy = 0EE79B342B2EDB9C00C1220C /* PBXContainerItemProxy */; + target = 0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */; + targetProxy = 0EC332D02B8A1808000B9C2F /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ -/* Begin PBXVariantGroup section */ - 0E0C072B236087A100155AAC /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 0E0C072A236087A100155AAC /* en */, - 0E0C072C236087C800155AAC /* it */, - 0EBE2FD02360F88C00F0D5AB /* zh-Hans */, - 0EBE2FD12360F88E00F0D5AB /* fr */, - 0EBE2FD22360F88F00F0D5AB /* nl */, - 0EBE2FD32360F89200F0D5AB /* de */, - 0EBE2FD42360F89300F0D5AB /* el */, - 0EBE2FD52360F89400F0D5AB /* pt */, - 0EBE2FD62360F89500F0D5AB /* ru */, - 0EBE2FD72360F89600F0D5AB /* es */, - 0EBE2FD82360F89600F0D5AB /* sv */, - 0E1C0A52238FFF97009FC087 /* pl */, - A373484D29DC4F4500D1613C /* uk */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - 0E9E5AE227B44CF1008C95DA /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 0E9E5AE327B44CF1008C95DA /* de */, - 0E9E5AE427B44CF1008C95DA /* el */, - 0E9E5AE527B44CF1008C95DA /* zh-Hans */, - 0E9E5AE627B44CF1008C95DA /* en */, - 0E9E5AE727B44CF1008C95DA /* es */, - 0E9E5AE827B44CF1008C95DA /* it */, - 0E9E5AE927B44CF1008C95DA /* sv */, - 0E9E5AEA27B44CF1008C95DA /* pl */, - 0E9E5AEB27B44CF1008C95DA /* ru */, - 0E9E5AEC27B44CF1008C95DA /* fr */, - 0E9E5AED27B44CF1008C95DA /* nl */, - 0E9E5AEE27B44CF1008C95DA /* pt */, - A373484E29DC504000D1613C /* uk */, - ); - name = Localizable.strings; - sourceTree = ""; - }; - 0EA591142733DDDA0096F796 /* Intents.intentdefinition */ = { - isa = PBXVariantGroup; - children = ( - 0EA591152733DDDA0096F796 /* Base */, - 0EA591182733DDF60096F796 /* en */, - 0EA5911A2733DDF80096F796 /* zh-Hans */, - 0EA5911C2733DDF80096F796 /* nl */, - 0EA5911E2733DDF90096F796 /* fr */, - 0EA591202733DDF90096F796 /* de */, - 0EA591222733DDFA0096F796 /* el */, - 0EA591242733DDFA0096F796 /* it */, - 0EA591262733DDFB0096F796 /* pl */, - 0EA591282733DDFB0096F796 /* pt */, - 0EA5912A2733DDFC0096F796 /* ru */, - 0EA5912C2733DDFC0096F796 /* es */, - 0EA5912E2733DDFD0096F796 /* sv */, - A373484F29DC52CA00D1613C /* uk */, - ); - name = Intents.intentdefinition; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ - 0E41BDA4286711C6006346B4 /* Debug */ = { + 0E06D19C2B87629200176E1D /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 0E8D852F2C328CA1005493DE /* Config.xcconfig */; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_ENTITLEMENTS = Passepartout/Launcher/Launcher.entitlements; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = Passepartout/Launcher/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_LAUNCHER_ID)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 0E41BDA5286711C6006346B4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_ENTITLEMENTS = Passepartout/Launcher/Launcher.entitlements; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = Passepartout/Launcher/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_LAUNCHER_ID)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 0E57F65320C83FC7008323CF /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0E23B4A12298559800304C30 /* Config.xcconfig */; - buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1778,14 +356,16 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CURRENT_PROJECT_VERSION = 3630; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = "$(CFG_TEAM_ID)"; - ENABLE_BITCODE = NO; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1799,29 +379,37 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MACOSX_DEPLOYMENT_TARGET = 12.0; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "$(CFG_DISPLAY_NAME)"; + INFOPLIST_KEY_NSHumanReadableCopyright = "$(CFG_COPYRIGHT)"; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; TVOS_DEPLOYMENT_TARGET = 17.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; - 0E57F65420C83FC7008323CF /* Release */ = { + 0E06D19D2B87629200176E1D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0E23B4A12298559800304C30 /* Config.xcconfig */; + baseConfigurationReference = 0E8D852F2C328CA1005493DE /* Config.xcconfig */; buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1848,13 +436,16 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "Apple Distribution"; - CURRENT_PROJECT_VERSION = 3630; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = "$(CFG_TEAM_ID)"; - ENABLE_BITCODE = NO; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -1862,410 +453,209 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MACOSX_DEPLOYMENT_TARGET = 12.0; - SDKROOT = iphoneos; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "$(CFG_DISPLAY_NAME)"; + INFOPLIST_KEY_NSHumanReadableCopyright = "$(CFG_COPYRIGHT)"; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; TVOS_DEPLOYMENT_TARGET = 17.0; - VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; - 0E57F65620C83FC7008323CF /* Debug */ = { + 0E06D19F2B87629200176E1D /* Debug */ = { isa = XCBuildConfiguration; 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; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3630; - INFOPLIST_FILE = Passepartout/App/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + CURRENT_PROJECT_VERSION = 3619; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Passepartout/App/App.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(TARGET_NAME)"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Davide De Rosa. All rights reserved."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = dummy; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 3.0.0; PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID)"; - PRODUCT_NAME = Passepartout; + PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout tvos"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.algoritmico.ios.Passepartout"; - "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; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout macos"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2,3"; }; name = Debug; }; - 0E57F65720C83FC7008323CF /* Release */ = { + 0E06D1A02B87629200176E1D /* Release */ = { isa = XCBuildConfiguration; 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; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3630; - INFOPLIST_FILE = Passepartout/App/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + CURRENT_PROJECT_VERSION = 3619; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Passepartout/App/App.plist; + INFOPLIST_KEY_CFBundleDisplayName = "$(TARGET_NAME)"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Davide De Rosa. All rights reserved."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = dummy; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 3.0.0; PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID)"; - PRODUCT_NAME = Passepartout; + PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match AppStore com.algoritmico.ios.Passepartout tvos"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.algoritmico.ios.Passepartout"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore com.algoritmico.ios.Passepartout catalyst"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = YES; - SWIFT_STRICT_CONCURRENCY = targeted; - }; - name = Release; - }; - 0ECB78DB285F52F700B0E460 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 3630; - DEAD_CODE_STRIPPING = YES; - INFOPLIST_FILE = Passepartout/Mac/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_MAC_ID)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = macosx; - SKIP_INSTALL = YES; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore com.algoritmico.ios.Passepartout macos"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = "Passepartout/Mac/Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - WRAPPER_EXTENSION = bundle; + TARGETED_DEVICE_FAMILY = "1,2,3"; }; - name = Debug; + name = Release; }; - 0ECB78DC285F52F700B0E460 /* Release */ = { + 0EC332D42B8A1808000B9C2F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 3630; - DEAD_CODE_STRIPPING = YES; - INFOPLIST_FILE = Passepartout/Mac/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements; + CURRENT_PROJECT_VERSION = 3619; + INFOPLIST_FILE = Passepartout/Tunnel/Tunnel.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", + "@executable_path/../../Frameworks", ); - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_MAC_ID)"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../../../../Frameworks"; + MARKETING_VERSION = 3.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_TUNNEL_ID)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = macosx; + "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.Tunnel tvos"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.algoritmico.ios.Passepartout.Tunnel"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout.Tunnel macos"; SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OBJC_BRIDGING_HEADER = "Passepartout/Mac/Bridging-Header.h"; - SWIFT_VERSION = 5.0; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - 0ECF71F527B6D9CD00CDB528 /* 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; - 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)"; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; }; name = Debug; }; - 0ECF71F627B6D9CD00CDB528 /* Release */ = { + 0EC332D52B8A1808000B9C2F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; + CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements; + CURRENT_PROJECT_VERSION = 3619; + INFOPLIST_FILE = Passepartout/Tunnel/Tunnel.plist; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../../Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../../../../Frameworks"; + MARKETING_VERSION = 3.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_TUNNEL_ID)"; PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 0ED2B34827D3C77800FD8EA9 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements; - CODE_SIGN_STYLE = Manual; - INFOPLIST_FILE = Passepartout/Tunnel/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).WireGuardTunnel"; - PRODUCT_NAME = PassepartoutWireGuardTunnel; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel tvos"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout.WireGuardTunnel catalyst"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match AppStore com.algoritmico.ios.Passepartout.Tunnel tvos"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.algoritmico.ios.Passepartout.Tunnel"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore com.algoritmico.ios.Passepartout.Tunnel macos"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = YES; - }; - name = Debug; - }; - 0ED2B34927D3C77800FD8EA9 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements; - CODE_SIGN_STYLE = Manual; - INFOPLIST_FILE = Passepartout/Tunnel/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).WireGuardTunnel"; - PRODUCT_NAME = PassepartoutWireGuardTunnel; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match AppStore com.algoritmico.ios.Passepartout.WireGuardTunnel tvos"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.algoritmico.ios.Passepartout.WireGuardTunnel"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore com.algoritmico.ios.Passepartout.WireGuardTunnel catalyst"; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = YES; - }; - name = Release; - }; - 0EDE8DCA20C86910004C739C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements; - CODE_SIGN_STYLE = Manual; - INFOPLIST_FILE = Passepartout/Tunnel/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).OpenVPNTunnel"; - PRODUCT_NAME = PassepartoutOpenVPNTunnel; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel tvos"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.algoritmico.ios.Passepartout.OpenVPNTunnel"; - "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; - }; - 0EDE8DCB20C86910004C739C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Passepartout/Tunnel/Tunnel.entitlements; - CODE_SIGN_STYLE = Manual; - INFOPLIST_FILE = Passepartout/Tunnel/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_APP_ID).OpenVPNTunnel"; - PRODUCT_NAME = PassepartoutOpenVPNTunnel; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]" = "match AppStore com.algoritmico.ios.Passepartout.OpenVPNTunnel tvos"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.algoritmico.ios.Passepartout.OpenVPNTunnel"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore 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; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 0E41BDA3286711C6006346B4 /* Build configuration list for PBXNativeTarget "PassepartoutLauncher" */ = { + 0E06D18A2B87629100176E1D /* Build configuration list for PBXProject "Passepartout" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0E41BDA4286711C6006346B4 /* Debug */, - 0E41BDA5286711C6006346B4 /* Release */, + 0E06D19C2B87629200176E1D /* Debug */, + 0E06D19D2B87629200176E1D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 0E57F63320C83FC5008323CF /* Build configuration list for PBXProject "Passepartout" */ = { + 0E06D19E2B87629200176E1D /* Build configuration list for PBXNativeTarget "Passepartout" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0E57F65320C83FC7008323CF /* Debug */, - 0E57F65420C83FC7008323CF /* Release */, + 0E06D19F2B87629200176E1D /* Debug */, + 0E06D1A02B87629200176E1D /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 0E57F65520C83FC7008323CF /* Build configuration list for PBXNativeTarget "Passepartout" */ = { + 0EC332D32B8A1808000B9C2F /* Build configuration list for PBXNativeTarget "PassepartoutTunnel" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0E57F65620C83FC7008323CF /* Debug */, - 0E57F65720C83FC7008323CF /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0ECB78DD285F52F700B0E460 /* Build configuration list for PBXNativeTarget "PassepartoutMac" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0ECB78DB285F52F700B0E460 /* Debug */, - 0ECB78DC285F52F700B0E460 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0ECF71F427B6D9CD00CDB528 /* Build configuration list for PBXLegacyTarget "WireGuardGo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0ECF71F527B6D9CD00CDB528 /* Debug */, - 0ECF71F627B6D9CD00CDB528 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0ED2B34727D3C77800FD8EA9 /* Build configuration list for PBXNativeTarget "WireGuardTunnel" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0ED2B34827D3C77800FD8EA9 /* Debug */, - 0ED2B34927D3C77800FD8EA9 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0EDE8DC920C86910004C739C /* Build configuration list for PBXNativeTarget "OpenVPNTunnel" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0EDE8DCA20C86910004C739C /* Debug */, - 0EDE8DCB20C86910004C739C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0EE79B312B2EDB5D00C1220C /* Build configuration list for PBXLegacyTarget "WireGuardGoTV" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0EE79B322B2EDB5D00C1220C /* Debug */, - 0EE79B332B2EDB5D00C1220C /* Release */, + 0EC332D42B8A1808000B9C2F /* Debug */, + 0EC332D52B8A1808000B9C2F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - 0E53249B27D28FC7002565C3 /* XCRemoteSwiftPackageReference "Kvitto" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Cocoanetics/Kvitto"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ - 0E53249C27D28FC7002565C3 /* Kvitto */ = { + 0E6C0A022BF4047100450362 /* AppLibrary */ = { isa = XCSwiftPackageProductDependency; - package = 0E53249B27D28FC7002565C3 /* XCRemoteSwiftPackageReference "Kvitto" */; - productName = Kvitto; + productName = AppLibrary; }; - 0E7A8C0B2A1D4A6100780F4B /* PassepartoutLibrary */ = { + 0E6C0A042BF4047600450362 /* TunnelLibrary */ = { isa = XCSwiftPackageProductDependency; - productName = PassepartoutLibrary; + productName = TunnelLibrary; }; - 0ED2B33827D3C49800FD8EA9 /* OpenVPNAppExtension */ = { + 0EBE80D92BF55C0E00E36A20 /* AppLibrary */ = { isa = XCSwiftPackageProductDependency; - productName = OpenVPNAppExtension; + productName = AppLibrary; }; - 0ED2B36627D3C9A300FD8EA9 /* WireGuardAppExtension */ = { + 0EBE80DB2BF55C0E00E36A20 /* TunnelLibrary */ = { isa = XCSwiftPackageProductDependency; - productName = WireGuardAppExtension; + productName = TunnelLibrary; }; /* End XCSwiftPackageProductDependency section */ }; - rootObject = 0E57F63020C83FC5008323CF /* Project object */; + rootObject = 0E06D1872B87629100176E1D /* Project object */; } diff --git a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 00144354..1c350d05 100644 --- a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,12 +1,21 @@ { "pins" : [ { - "identity" : "generic-json-swift", + "identity" : "dtfoundation", "kind" : "remoteSourceControl", - "location" : "https://github.com/zoul/generic-json-swift", + "location" : "https://github.com/Cocoanetics/DTFoundation.git", "state" : { - "revision" : "0a06575f4038b504e78ac330913d920f1630f510", - "version" : "2.0.2" + "revision" : "76062513434421cb6c8a1ae1d4f8368a7ebc2da3", + "version" : "1.7.18" + } + }, + { + "identity" : "kvitto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Cocoanetics/Kvitto", + "state" : { + "revision" : "88888674d772ddcf19671159ed0022cb0bc37be2", + "version" : "1.0.6" } }, { @@ -14,25 +23,44 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/passepartoutvpn/openssl-apple", "state" : { - "revision" : "026702febcaebcbf9ea68f2fa66b017eba998cdf", - "version" : "3.2.105" + "revision" : "0edc07c7a0e4ec2ca0f448dd68314241ccc925b3", + "version" : "3.2.107" } }, { - "identity" : "swiftybeaver", + "identity" : "passepartoutkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/SwiftyBeaver/SwiftyBeaver", + "location" : "git@github.com:passepartoutvpn/passepartoutkit", "state" : { - "revision" : "12b5acf96d98f91d50de447369bd18df74600f1a", - "version" : "1.9.6" + "revision" : "2c32459d6a669e8feed0e6ea2d1250ef72364aa3", + "version" : "0.7.0" } }, { - "identity" : "tunnelkit", + "identity" : "passepartoutkit-openvpn-openssl", "kind" : "remoteSourceControl", - "location" : "https://github.com/passepartoutvpn/tunnelkit", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-openvpn-openssl", "state" : { - "revision" : "6ab1759e048867fbca9bd5d33f2dc7eb1fa79ca6" + "revision" : "a3092a6ee0a63f666aa47ef3f0f50c324a64598d", + "version" : "0.6.0" + } + }, + { + "identity" : "passepartoutkit-wireguard-go", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-wireguard-go", + "state" : { + "revision" : "2cbd6023300d2dcc3f6f68de4812cf390421ec35", + "version" : "0.6.2" + } + }, + { + "identity" : "wg-go-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/wg-go-apple", + "state" : { + "revision" : "860e82efaf261da37483a5f51555be83e5a79ad3", + "version" : "0.0.20240714" } }, { @@ -40,7 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/passepartoutvpn/wireguard-apple", "state" : { - "revision" : "b79f0f150356d8200a64922ecf041dd020140aa0" + "revision" : "a896f784bc5ed94f29d97e376be5cfa08d4a5d44", + "version" : "1.1.1" } } ], diff --git a/Passepartout.xcodeproj/xcshareddata/xcschemes/Passepartout.xcscheme b/Passepartout.xcodeproj/xcshareddata/xcschemes/Passepartout.xcscheme index 58d84cb2..c9a6abfa 100644 --- a/Passepartout.xcodeproj/xcshareddata/xcschemes/Passepartout.xcscheme +++ b/Passepartout.xcodeproj/xcshareddata/xcschemes/Passepartout.xcscheme @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - @@ -103,35 +51,33 @@ + argument = "-com.apple.CoreData.SQLDebug 1" + isEnabled = "NO"> - - - - - - + + + + @@ -146,7 +92,7 @@ runnableDebuggingMode = "0"> diff --git a/Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutLauncher.xcscheme b/Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutTunnel.xcscheme similarity index 56% rename from Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutLauncher.xcscheme rename to Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutTunnel.xcscheme index 0012a06b..9a352d81 100644 --- a/Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutLauncher.xcscheme +++ b/Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutTunnel.xcscheme @@ -1,7 +1,8 @@ + LastUpgradeVersion = "1540" + wasCreatedForAppExtension = "YES" + version = "2.0"> @@ -14,9 +15,23 @@ buildForAnalyzing = "YES"> + + + + @@ -26,27 +41,28 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> - - + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + allowLocationSimulation = "YES" + launchAutomaticallySubstyle = "2"> @@ -56,14 +72,16 @@ shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" - debugDocumentVersioning = "YES"> + debugDocumentVersioning = "YES" + askForAppToLaunch = "Yes" + launchAutomaticallySubstyle = "2"> diff --git a/Passepartout/App/App.entitlements b/Passepartout/App/App.entitlements index 96ee78c8..090aad11 100644 --- a/Passepartout/App/App.entitlements +++ b/Passepartout/App/App.entitlements @@ -2,40 +2,29 @@ - aps-environment - development - com.apple.developer.icloud-container-identifiers - - iCloud.com.algoritmico.Passepartout - iCloud.com.algoritmico.Passepartout.Shared - - com.apple.developer.icloud-services - - CloudKit - com.apple.developer.networking.networkextension packet-tunnel-provider com.apple.developer.networking.wifi-info - com.apple.developer.siri - - com.apple.security.app-sandbox - com.apple.security.application-groups - group.$(CFG_GROUP_ID) + $(CFG_GROUP_ID) - com.apple.security.files.user-selected.read-write + com.apple.security.app-sandbox - com.apple.security.network.client + com.apple.security.files.user-selected.read-only com.apple.security.personal-information.location + com.apple.security.network.client + + com.apple.security.network.server + keychain-access-groups - $(AppIdentifierPrefix)group.com.algoritmico.Passepartout + $(AppIdentifierPrefix)$(CFG_GROUP_ID) diff --git a/Passepartout/App/App.plist b/Passepartout/App/App.plist new file mode 100644 index 00000000..fbcba030 --- /dev/null +++ b/Passepartout/App/App.plist @@ -0,0 +1,27 @@ + + + + + AppConfig + + appId + $(CFG_APP_ID) + appStoreId + $(CFG_APP_STORE_ID) + groupId + $(CFG_GROUP_ID) + iapBundlePrefix + $(CFG_IAP_BUNDLE_PREFIX) + keychainGroupId + $(CFG_TEAM_ID).$(CFG_GROUP_ID) + profilesContainerName + $(CFG_PROFILES_CONTAINER_NAME) + teamId + $(CFG_TEAM_ID) + tunnelId + $(CFG_TUNNEL_ID) + + ITSAppUsesNonExemptEncryption + + + diff --git a/Passepartout/App/AppDelegate.swift b/Passepartout/App/AppDelegate.swift index d85fd82a..7d897c32 100644 --- a/Passepartout/App/AppDelegate.swift +++ b/Passepartout/App/AppDelegate.swift @@ -2,7 +2,7 @@ // AppDelegate.swift // Passepartout // -// Created by Davide De Rosa on 6/25/22. +// Created by Davide De Rosa on 9/18/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,37 +23,31 @@ // along with Passepartout. If not, see . // -import Foundation -import PassepartoutLibrary +#if os(iOS) + import UIKit -final class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject { - private let mac = MacBundle.shared +final class AppDelegate: NSObject, UIApplicationDelegate { +} - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { - #if targetEnvironment(macCatalyst) - mac.configure() - mac.menu.install() - if mac.utils.isStartedByLauncher { - mac.utils.sendAppToBackground() - } - #endif - return true +#else + +import AppKit +import CommonLibrary +import PassepartoutKit + +final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + NSWindow.allowsAutomaticWindowTabbing = false + + // XXX: hack to only retain "Edit" menu + NSApp.mainMenu?.items = NSApp.mainMenu?.items.filter { + [BundleConfiguration.main.displayName, "Edit"].contains($0.title) + } ?? [] } - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - let sceneConfiguration = UISceneConfiguration(name: "SceneDelegate", sessionRole: connectingSceneSession.role) - sceneConfiguration.delegateClass = SceneDelegate.self - return sceneConfiguration - } - - override func buildMenu(with builder: UIMenuBuilder) { - super.buildMenu(with: builder) - builder.remove(menu: .file) - builder.remove(menu: .services) - builder.remove(menu: .format) - builder.remove(menu: .toolbar) - builder.remove(menu: .view) - builder.remove(menu: .help) + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + true } } +#endif diff --git a/Passepartout/App/Assets.xcassets/AccentColor.colorset/Contents.json b/Passepartout/App/Assets.xcassets/AccentColor.colorset/Contents.json index 06515a89..eb878970 100644 --- a/Passepartout/App/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/Passepartout/App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -1,15 +1,6 @@ { "colors" : [ { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x68", - "green" : "0x9C", - "red" : "0xD6" - } - }, "idiom" : "universal" } ], diff --git a/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac.png b/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac.png index 83a6038a..54778b9a 100644 Binary files a/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac.png and b/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac.png differ diff --git a/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac@2x.png b/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac@2x.png new file mode 100644 index 00000000..83a6038a Binary files /dev/null and b/Passepartout/App/Assets.xcassets/AppIcon.appiconset/AppIcon-mac@2x.png differ diff --git a/Passepartout/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Passepartout/App/Assets.xcassets/AppIcon.appiconset/Contents.json index d5e8c6b4..5954f6c6 100644 --- a/Passepartout/App/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Passepartout/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -47,12 +47,13 @@ "size" : "256x256" }, { + "filename" : "AppIcon-mac.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "AppIcon-mac.png", + "filename" : "AppIcon-mac@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/Passepartout/App/Assets.xcassets/LightTextColor.colorset/Contents.json b/Passepartout/App/Assets.xcassets/LightTextColor.colorset/Contents.json deleted file mode 100644 index 97650a1a..00000000 --- a/Passepartout/App/Assets.xcassets/LightTextColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Assets.xcassets/PrimaryColor.colorset/Contents.json b/Passepartout/App/Assets.xcassets/PrimaryColor.colorset/Contents.json deleted file mode 100644 index 546e2723..00000000 --- a/Passepartout/App/Assets.xcassets/PrimaryColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.443", - "green" : "0.365", - "red" : "0.318" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Constants/Constants+App.swift b/Passepartout/App/Constants/Constants+App.swift deleted file mode 100644 index 04c2dc09..00000000 --- a/Passepartout/App/Constants/Constants+App.swift +++ /dev/null @@ -1,259 +0,0 @@ -// -// Constants+App.swift -// Passepartout -// -// Created by Davide De Rosa on 9/15/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary -import UniformTypeIdentifiers - -extension Constants { - enum App { - static var appId: String { - guard let identifier = Bundle.main.infoDictionary?[kCFBundleIdentifierKey as String] as? String else { - fatalError("Missing kCFBundleIdentifierKey from Info.plist") - } - return identifier - } - - static let appStoreId: String = bundleConfig("appstore_id") - - static let appGroupId: String = bundleConfig("group_id") - } - - 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" - } - - enum Plugins { - static let macBridgeName = "PassepartoutMac.bundle" - } - - enum InApp { - static var overriddenAppType: AppType? { - if let envString = ProcessInfo.processInfo.environment["APP_TYPE"], - let envValue = Int(envString), - let testAppType = AppType(rawValue: envValue) { - - return testAppType - } - if let infoValue: Int = bundleConfig("app_type"), - let testAppType = AppType(rawValue: infoValue) { - - return testAppType - } - return nil - } - - #if targetEnvironment(macCatalyst) - static let buildProducts = BuildProducts { - if $0 <= 3000 { - return [.networkSettings] - } - return [] - } - #else - static let buildProducts = BuildProducts { - if $0 <= 2016 { - return [.fullVersion_iOS] - } else if $0 <= 3000 { - return [.networkSettings] - } - return [] - } - #endif - - static let tvLimitedMinutes = 10 - } -} - -extension Constants { - enum Activities { - static let enableVPN = "EnableVPNIntent" - - static let disableVPN = "DisableVPNIntent" - - static let connectVPN = "ConnectVPNIntent" - - static let moveToLocation = "MoveToLocationIntent" - - static let trustCellularNetwork = "TrustCellularNetworkIntent" - - static let trustCurrentNetwork = "TrustCurrentNetworkIntent" - - static let untrustCellularNetwork = "UntrustCellularNetworkIntent" - - static let untrustCurrentNetwork = "UntrustCurrentNetworkIntent" - } -} - -extension Constants { - enum Domain { - static let name = "passepartoutvpn.app" - } - - enum Services { - static let version = "v5" - - private static let connectivityStrings: [String] = [ - "https://www.amazon.com", - "https://www.google.com", - "https://www.twitter.com", - "https://www.facebook.com", - "https://www.instagram.com" - ] - - static let connectivityURL = URL(string: connectivityStrings.randomElement()!)! - - static let connectivityTimeout: TimeInterval = 10.0 - } - - enum Persistence { - static let profilesContainerName = "Profiles" - - static let sharedProfilesContainerName = "SharedProfiles" - - static let providersContainerName = "Providers" - } - - // milliseconds - enum RateLimit { - static let providerManager = 10000 - - static let vpnToggle = 500 - } - - enum Log { - enum App { - static let url = containerURL(filename: "App.log") - - static let format = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M" - } - - enum Tunnel { - static let path = containerPath(filename: "Tunnel.log") - - static let format = "$DHH:mm:ss$d - $M" - } - - private static let parentPath = "Library/Caches" - - static let level: LoggerLevel = { - guard let levelString = ProcessInfo.processInfo.environment["LOG_LEVEL"], - let levelNum = Int(levelString) else { - return .debug - } - return .init(rawValue: levelNum) ?? .debug - }() - - static let maxBytes = 100000 - - static let refreshInterval: TimeInterval = 5.0 - - private static func containerURL(filename: String) -> URL { - Files.containerURL - .appendingPathComponent(parentPath) - .appendingPathComponent(filename) - } - - private static func containerPath(filename: String) -> String { - [parentPath, filename].joined(separator: "/") - } - } - - enum URLs { - static let readme = Repos.apple.appendingPathComponent("blob/master/README.md") - - static let changelog = Repos.apple.appendingPathComponent("blob/master/CHANGELOG.md") - - static let filetypes: [UTType] = [.item] - - static let website = URL(string: "https://\(Domain.name)")! - - static let faq = website.appendingPathComponent("faq") - - static let disclaimer = website.appendingPathComponent("disclaimer") - - static let privacyPolicy = website.appendingPathComponent("privacy") - - static let donate = website.appendingPathComponent("donate") - - static let subreddit = URL(string: "https://www.reddit.com/r/passepartout")! - - static let twitch = URL(string: "twitch://stream/keeshux")! - - static let twitchFallback = URL(string: "https://twitch.tv/keeshux")! - - static let githubSponsors = URL(string: "https://www.github.com/sponsors/passepartoutvpn")! - } - - enum Repos { - private static let githubRoot = URL(string: "https://github.com/passepartoutvpn/")! - - private static let githubRawRoot = URL(string: "https://\(Domain.name)/")! - - private static func github(repo: String) -> URL { - githubRoot.appendingPathComponent(repo) - } - - private static func githubRaw(repo: String) -> URL { - githubRawRoot.appendingPathComponent(repo) - } - - static let apple = github(repo: "passepartout-apple") - - static let api = githubRaw(repo: "api") - } - - // milliseconds - enum Delays { - static let scrolling = 100 - -// @available(*, deprecated, message: "File importer stops showing again after closing with swipe down") - static let xxxPresentFileImporter = 200 - } - - enum Rating { - #if targetEnvironment(macCatalyst) - static let eventCount = 10 - #else - static let eventCount = 20 - #endif - } -} - -extension Constants { - enum Files { - fileprivate static var containerURL: URL { - guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: App.appGroupId) else { - print("Unable to access App Group container") - return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - } - return url - } - } -} diff --git a/Passepartout/App/Constants/Constants+Library.swift b/Passepartout/App/Constants/Constants+Library.swift deleted file mode 100644 index 0659cf4c..00000000 --- a/Passepartout/App/Constants/Constants+Library.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// Constants+Library.swift -// Passepartout -// -// Created by Davide De Rosa on 6/25/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -extension Constants.App { - static func tunnelBundleId(_ vpnProtocol: VPNProtocolType) -> String { - guard let identifier = Bundle.main.infoDictionary?[kCFBundleIdentifierKey as String] as? String else { - fatalError("Missing kCFBundleIdentifierKey from Info.plist") - } - switch vpnProtocol { - case .openVPN: - return "\(identifier).OpenVPNTunnel" - - case .wireGuard: - return "\(identifier).WireGuardTunnel" - } - } -} - -extension Constants.URLs { - static let openVPNGuidances: [ProviderName: String] = [ - .protonvpn: "https://account.protonvpn.com/settings", - .surfshark: "https://my.surfshark.com/vpn/manual-setup/main", - .torguard: "https://torguard.net/clientarea.php?action=changepw", - .windscribe: "https://windscribe.com/getconfig/openvpn" - ] - - static let referrals: [ProviderName: String] = [ - .hideme: "https://member.hide.me/en/checkout?plan=new_default_prices&coupon=6CB-BDB-802&duration=24", - .mullvad: "https://mullvad.net/en/account/create/", - .nordvpn: "https://go.nordvpn.net/SH21Z", - .pia: "https://www.privateinternetaccess.com/pages/buy-vpn/", - .protonvpn: "https://proton.go2cloud.org/SHZ", - .torguard: "https://torguard.net/", - .tunnelbear: "https://www.tunnelbear.com/", - .vyprvpn: "https://www.vyprvpn.com/", - .windscribe: "https://secure.link/kCsD0prd" - ] -} diff --git a/Passepartout/App/Constants/Theme.swift b/Passepartout/App/Constants/Theme.swift deleted file mode 100644 index e6687dbe..00000000 --- a/Passepartout/App/Constants/Theme.swift +++ /dev/null @@ -1,648 +0,0 @@ -// -// Theme.swift -// Passepartout -// -// Created by Davide De Rosa on 2/24/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import LocalAuthentication -#endif -import PassepartoutLibrary -import SwiftUI - -extension View { - var themeIdiom: UIUserInterfaceIdiom { - UIDevice.current.userInterfaceIdiom - } - - 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 - } -} - -// MARK: Global - -extension View { - func themeGlobal() -> some View { - themeNavigationViewStyle() - #if !os(tvOS) - #if !targetEnvironment(macCatalyst) - .themeLockScreen() - #endif - .themeTint() - .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() - #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 - private func themeNavigationViewStyle() -> some View { - switch themeIdiom { - case .phone: - navigationViewStyle(.stack) - - default: - navigationViewStyle(.automatic) - } - } - - @ViewBuilder - private func themeSidebarListStyle() -> some View { - #if !os(tvOS) - switch themeIdiom { - case .phone: - listStyle(.insetGrouped) - - default: - listStyle(.sidebar) - } - #else - self - #endif - } - - func themeTint() -> some View { - tint(.accentColor) - } - - func themeListSelectionColor(isSelected: Bool) -> some View { - let background = isSelected ? Color.gray.opacity(0.6) : .clear - return listRowBackground(background.themeRounded()) - } - - private func themeListStyleValue() -> some ListStyle { - #if !os(tvOS) - .insetGrouped - #else - PlainListStyle() - #endif - } - - private func themeToggleStyleValue() -> some ToggleStyle { - #if !os(tvOS) - .switch - #else - DefaultToggleStyle() - #endif - } -} - -// MARK: Colors - -extension View { - fileprivate var themePrimaryBackgroundColor: Color { - Color(.primary) - } - - fileprivate var themeSecondaryColor: Color { - .secondary - } - - fileprivate var themeLightTextColor: Color { - Color(.lightText) - } - - fileprivate var themeErrorColor: Color { - .red - } - - private func themeColor(_ string: String?, validator: (String) throws -> Void) -> Color? { - guard let string = string else { - return nil - } - do { - try validator(string) - return nil - } catch { - return themeErrorColor - } - } -} - -// MARK: Images - -extension View { - var themeAssetsLogoImage: String { - "Logo" - } - - var themeCheckmarkImage: String { - "checkmark" - } - - var themeShareImage: String { - "square.and.arrow.up" - } - - var themeCopyImage: String { - "doc.on.doc" - } - - var themeCloseImage: String { - "xmark" - } - - var themeConceilImage: String { - "eye.slash" - } - - var themeRevealImage: String { - "eye" - } - - var themeAppleTVImage: String { - "tv" - } - - // MARK: Organizer - - func themeAssetsProviderImage(_ providerName: ProviderName) -> String { - "providers/\(providerName)" - } - - func themeAssetsCountryImage(_ countryCode: String) -> String { - "flags/\(countryCode.lowercased())" - } - - var themeProviderImage: String { - "externaldrive.connected.to.line.below" - } - - var themeHostFilesImage: String { - "folder" - } - - var themeHostTextImage: String { - "text.justify" - } - - var themeSettingsImage: String { - "gearshape" - } - - var themeDonateImage: String { - "giftcard" - } - - var themeRedditImage: String { - "person.3" - } - - var themeWriteReviewImage: String { - "star" - } - - var themeAddMenuImage: String { - "plus" - } - - var themeProfileActiveImage: String { - "checkmark.circle" - } - - var themeProfileConnectedImage: String { - "circle.fill" - } - - var themeProfileInactiveImage: String { - "circle" - } - - // MARK: Profile - - var themeSettingsMenuImage: String { - "ellipsis.circle" - } - - var themeReconnectImage: String { - "arrow.clockwise" - } - - var themeShortcutsImage: String { - "mic" - } - - var themeRenameProfileImage: String { - "highlighter" -// "character.cursor.ibeam" - } - - var themeDuplicateImage: String { - "doc.on.doc" - } - - var themeUninstallImage: String { - "arrow.uturn.down" - } - - var themeDeleteImage: String { - "trash" - } - - var themeVPNProtocolImage: String { - "bolt" -// "waveform.path.ecg" -// "message.and.waveform.fill" -// "pc" -// "captions.bubble.fill" - } - - var themeEndpointImage: String { - "link" - } - - var themeAccountImage: String { - "person" - } - - var themeProviderLocationImage: String { - "location" - } - - var themeProviderPresetImage: String { - "slider.horizontal.3" - } - - var themeNetworkSettingsImage: String { -// "network" - "globe" - } - - var themeOnDemandImage: String { - "wifi" - } - - var themeDiagnosticsImage: String { - "bandage.fill" - } - - var themeFAQImage: String { - "questionmark.diamond" - } - - func themeFavoritesImage(_ active: Bool) -> String { - active ? "bookmark.fill" : "bookmark" - } - - func themeFavoriteActionImage(_ doFavorite: Bool) -> String { - doFavorite ? "bookmark" : "bookmark.slash.fill" - } -} - -extension String { - var asAssetImage: Image { - Image(self) - } - - var asSystemImage: Image { - Image(systemName: self) - } -} - -// MARK: Styles - -extension View { - func themeAccentForegroundStyle() -> some View { - foregroundColor(.accentColor) - } - - var themePrimaryBackground: some View { - themePrimaryBackgroundColor - .ignoresSafeArea() - } - - func themeSecondaryTextStyle() -> some View { - foregroundColor(themeSecondaryColor) - } - - func themeLightTextStyle() -> some View { - foregroundColor(themeLightTextColor) - } - - func themePrimaryTintStyle() -> some View { - tint(themePrimaryBackgroundColor) - } - - func themeDestructiveTintStyle() -> some View { - tint(themeErrorColor) - } - - func themeTextButtonStyle() -> some View { - accentColor(.primary) - } - - func themeLongTextStyle() -> some View { - lineLimit(1) - .truncationMode(.middle) - } - - func themeRawTextStyle() -> some View { - disableAutocorrection(true) - .autocapitalization(.none) - } - - func themeInformativeTextStyle() -> some View { - multilineTextAlignment(.center) - .font(.title) - .foregroundColor(themeSecondaryColor) - } - - func themeCellTitleStyle() -> some View { - font(.headline) - } - - func themeCellSubtitleStyle() -> some View { - font(.subheadline) - } - - func themeDebugLogStyle() -> some View { - font(.system(size: 13, weight: .medium, design: .monospaced)) - } - - func themeRounded() -> some View { - clipShape(.rect(cornerRadius: 10.0)) - } -} - -// MARK: Shortcuts - -#if !os(tvOS) -extension ShortcutType { - var themeImageName: String { - switch self { - case .enableVPN: - return "power" - - case .disableVPN: - return "xmark" - - case .reconnectVPN: - return "arrow.clockwise" - } - } -} -#endif - -// MARK: Animations - -extension View { - func themeAnimation(on value: V) -> some View { - animation(.default, value: value) - } -} - -extension Binding { - func themeAnimation() -> Binding { - animation(.default) - } -} - -// MARK: Shortcuts - -extension View { - func themeCloseItem(presentationMode: Binding) -> some ToolbarContent { - ToolbarItem(placement: .cancellationAction) { - Button { - presentationMode.wrappedValue.dismiss() - } label: { - themeCloseImage.asSystemImage - } - } - } - - func themeCloseItem(isPresented: Binding) -> some ToolbarContent { - ToolbarItem(placement: .cancellationAction) { - Button { - isPresented.wrappedValue = false - } label: { - themeCloseImage.asSystemImage - } - } - } - - func themeSaveButtonLabel() -> some View { - Text(L10n.Global.Strings.save) - } - - func themeSecureField(_ placeholder: String, text: Binding, contentType: UITextContentType = .password) -> some View { - RevealingSecureField(placeholder, text: text) { - themeConceilImage.asSystemImage - .themeAccentForegroundStyle() - } revealImage: { - themeRevealImage.asSystemImage - .themeAccentForegroundStyle() - }.textContentType(contentType) - .themeRawTextStyle() - } - - func themeTextPicker(_ title: String, selection: Binding, values: [T], description: @escaping (T) -> String) -> some View { - StyledPicker(title: title, selection: selection, values: values) { - Text(description($0)) - } selectionLabel: { - Text(description($0)) - .foregroundColor(themeSecondaryColor) - } listStyle: { - themeListStyleValue() - } - } - - func themeLongContentLinkDefault(_ title: String, content: Binding) -> some View { - LongContentLink(title, content: content) { - Text($0) - .foregroundColor(themeSecondaryColor) - } - } - - func themeLongContentLink(_ title: String, content: Binding, withPreview preview: String? = nil) -> some View { - LongContentLink(title, content: content, preview: preview) { - Text(preview != nil ? $0 : "") - .foregroundColor(themeSecondaryColor) - } - } - - @ViewBuilder - func themeErrorMessage(_ message: String?) -> some View { - if let message = message { - if message.last != "." { - Text("\(message).") - .foregroundColor(themeErrorColor) - } else { - Text(message) - .foregroundColor(themeErrorColor) - } - } else { - EmptyView() - } - } -} - -// MARK: Lock screen - -#if !os(tvOS) -extension View { - func themeLockScreen() -> some View { - @AppStorage(AppPreference.locksInBackground.key) var locksInBackground = false - return LockableView( - locksInBackground: $locksInBackground, - content: { - self - }, - lockedContent: LogoView.init, - unlockBlock: Self.themeUnlockScreenBlock - ) - } - - private static func themeUnlockScreenBlock() async -> Bool { - let context = LAContext() - let policy: LAPolicy = .deviceOwnerAuthentication - var error: NSError? - guard context.canEvaluatePolicy(policy, error: &error) else { - return true - } - do { - let isAuthorized = try await context.evaluatePolicy( - policy, - localizedReason: L10n.Global.Messages.unlockApp - ) - return isAuthorized - } catch { - return false - } - } -} -#endif - -// MARK: Validation - -extension View { - func themeValidProfileName() -> some View { - themeRawTextStyle() - } - - func themeValidURL(_ urlString: String?) -> some View { - themeValidating(urlString, validator: Validators.url) - .keyboardType(.asciiCapable) - .themeRawTextStyle() - } - - func themeValidIPAddress(_ ipAddress: String?) -> some View { - themeValidating(ipAddress, validator: Validators.ipAddress) - .keyboardType(.numbersAndPunctuation) - .themeRawTextStyle() - } - - func themeValidSocketPort(_ port: String?) -> some View { - themeValidating(port, validator: Validators.socketPort) - .keyboardType(.numberPad) - } - - func themeValidDomainName(_ domainName: String?) -> some View { - themeValidating(domainName, validator: Validators.domainName) - .keyboardType(.asciiCapable) - .themeRawTextStyle() - } - - func themeValidWildcardDomainName(_ domainName: String?) -> some View { - themeValidating(domainName, validator: Validators.wildcardDomainName) - .keyboardType(.asciiCapable) - .themeRawTextStyle() - } - - func themeValidDNSOverTLSServerName(_ string: String?) -> some View { - themeValidating(string, validator: Validators.dnsOverTLSServerName) - .keyboardType(.asciiCapable) - .themeRawTextStyle() - } - - func themeValidSSID(_ text: String?) -> some View { - themeValidating(text, validator: Validators.notEmpty) - .keyboardType(.asciiCapable) - .themeRawTextStyle() - } - - private func themeValidating(_ string: String?, validator: (String) throws -> Void) -> some View { - foregroundColor(themeColor(string, validator: validator)) - } -} - -// MARK: Hacks - -extension View { - @available(*, deprecated, message: "Mitigates multiline text truncation (1.0 does not work though)") - func xxxThemeTruncation() -> some View { - minimumScaleFactor(0.5) - } -} diff --git a/Passepartout/App/Context/AppContext+Shared.swift b/Passepartout/App/Context/AppContext+Shared.swift deleted file mode 100644 index c016ef90..00000000 --- a/Passepartout/App/Context/AppContext+Shared.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// AppContext+Shared.swift -// Passepartout -// -// Created by Davide De Rosa on 6/15/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -// safer alternative to @EnvironmentObject - -// MARK: App - -extension AppContext { - static let shared = AppContext(store: UserDefaultsStore(defaults: .standard, key: \.key)) -} - -extension UpgradeManager { - static let shared = AppContext.shared.upgradeManager -} - -extension ProductManager { - static let shared = AppContext.shared.productManager -} - -extension PersistenceManager { - static let shared = AppContext.shared.persistenceManager -} - -// MARK: App -> Core - -extension ProfileManager { - static let shared = AppContext.shared.profileManager -} - -extension ProviderManager { - static let shared = AppContext.shared.providerManager -} - -extension VPNManager { - static let shared = AppContext.shared.vpnManager -} - -extension ObservableVPNState { - - @MainActor - static let shared = AppContext.shared.vpnManager.currentState -} diff --git a/Passepartout/App/Context/AppContext.swift b/Passepartout/App/Context/AppContext.swift deleted file mode 100644 index 47ba9a37..00000000 --- a/Passepartout/App/Context/AppContext.swift +++ /dev/null @@ -1,183 +0,0 @@ -// -// AppContext.swift -// Passepartout -// -// Created by Davide De Rosa on 3/17/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Combine -import Foundation -import PassepartoutLibrary - -@MainActor -final class AppContext { - private let coreContext: CoreContext - - let upgradeManager: UpgradeManager - - let productManager: ProductManager - - let persistenceManager: PersistenceManager - - private let reviewer: Reviewer - - private var cancellables: Set = [] - - init(store: KeyValueStore) { - let logger = SwiftyBeaverLogger( - logFile: Constants.Log.App.url, - logLevel: Constants.Log.level, - logFormat: Constants.Log.App.format - ) - Passepartout.shared.logger = logger - pp_log.info("Logging to: \(logger.logFile!)") - - upgradeManager = UpgradeManager( - store: store, - strategy: DefaultUpgradeManagerStrategy() - ) - upgradeManager.migrate(toVersion: Constants.Global.appVersionNumber) - - productManager = ProductManager( - inApp: StoreKitInApp(), - receiptReader: StoreKitReceiptReader(), - overriddenAppType: Constants.InApp.overriddenAppType, - buildProducts: Constants.InApp.buildProducts - ) - - persistenceManager = PersistenceManager( - store: store, - ckContainerId: Constants.CloudKit.containerId, - ckSharedContainerId: Constants.CloudKit.sharedContainerId, - ckCoreDataZone: Constants.CloudKit.coreDataZone - ) - - reviewer = Reviewer() - reviewer.eventCountBeforeRating = Constants.Rating.eventCount - - coreContext = CoreContext(persistenceManager: persistenceManager) - - // post - - configureObjects() - } - - var providerManager: ProviderManager { - coreContext.providerManager - } - - var profileManager: ProfileManager { - coreContext.profileManager - } - - var vpnManager: VPNManager { - coreContext.vpnManager - } -} - -private extension AppContext { - func configureObjects() { - coreContext.profileManager.willSaveSharedProfile = { [unowned self] in - willSaveSharedProfile(withNewProfile: $0, existingProfile: $1) - } - - coreContext.vpnManager.isOnDemandRulesSupported = { - self.isEligibleForOnDemandRules() - } - coreContext.vpnManager.isNetworkSettingsSupported = { - 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) - .sink { [weak self] in - if $0 == .connected { - pp_log.info("VPN successful connection, report to Reviewer") - self?.reviewer.reportEvent() - } - }.store(in: &cancellables) - } - - // eligibility: ignore network settings if ineligible - func isEligibleForNetworkSettings() -> Bool { - guard productManager.isEligible(forFeature: .networkSettings) else { - pp_log.warning("Ignore network settings, not eligible") - return false - } - return true - } - - // eligibility: reset on demand rules if no trusted networks - func isEligibleForOnDemandRules() -> Bool { - guard productManager.isEligible(forFeature: .trustedNetworks) else { - pp_log.warning("Ignore on demand rules, not eligible for trusted networks") - return false - } - 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 existingProfile, let currentExpirationDate = existingProfile.connectionExpirationDate { - remainingMinutes = Int(currentExpirationDate.timeIntervalSinceNow / 60.0) - expirationDate = currentExpirationDate - - restricted.connectionExpirationDate = 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 - } -} diff --git a/Passepartout/App/Context/CoreContext.swift b/Passepartout/App/Context/CoreContext.swift deleted file mode 100644 index 088585d3..00000000 --- a/Passepartout/App/Context/CoreContext.swift +++ /dev/null @@ -1,145 +0,0 @@ -// -// CoreContext.swift -// Passepartout -// -// Created by Davide De Rosa on 6/4/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Combine -import Foundation -import PassepartoutLibrary -import TunnelKitCore -import TunnelKitManager - -@MainActor -final class CoreContext { - let store: KeyValueStore - - let providerManager: ProviderManager - - let profileManager: ProfileManager - - let vpnManager: VPNManager - - private var cancellables: Set = [] - - 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 - ) - - let remoteProvidersStrategy = APIRemoteProvidersStrategy( - appBuild: Constants.Global.appBuildNumber, - bundleServices: APIWebServices.bundledServices( - withVersion: Constants.Services.version - ), - remoteServices: APIWebServices( - Constants.Services.version, - Constants.Repos.api, - timeout: Constants.Services.connectivityTimeout - ), - webServicesRepository: providersPersistence.webServicesRepository() - ) - providerManager = ProviderManager( - localProvidersRepository: providersPersistence.localProvidersRepository(), - 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: profileRepository, - sharedProfileRepository: sharedProfileRepository, - keychain: KeychainSecretRepository(appGroup: Constants.App.appGroupId), - keychainEntry: Unlocalized.Keychain.passwordEntry, - keychainLabel: Unlocalized.Keychain.passwordLabel - ) - - #if targetEnvironment(simulator) - let vpn = MockVPN() - #else - let vpn = NetworkExtensionVPN() - #endif - let vpnManagerStrategy = TunnelKitVPNManagerStrategy( - appGroup: Constants.App.appGroupId, - tunnelBundleIdentifier: Constants.App.tunnelBundleId, - vpn: vpn - ) - vpnManager = VPNManager( - store: store, - profileManager: profileManager, - providerManager: providerManager, - strategy: vpnManagerStrategy - ) - - // post - - configureObjects(persistenceManager: persistenceManager) - } -} - -private extension CoreContext { - func configureObjects(persistenceManager: PersistenceManager) { - providerManager.rateLimitMilliseconds = Constants.RateLimit.providerManager - vpnManager.tunnelLogPath = Constants.Log.Tunnel.path - vpnManager.tunnelLogFormat = Constants.Log.Tunnel.format - - profileManager.observeUpdates() - vpnManager.observeUpdates() - - CoreConfiguration.masksPrivateData = vpnManager.masksPrivateData - vpnManager.didUpdatePreferences - .sink { - CoreConfiguration.masksPrivateData = $0.masksPrivateData - }.store(in: &cancellables) - - persistenceManager.didChangePersistence - .sink { [weak self] in - self?.reloadPersistenceObjects(persistenceManager: persistenceManager) - }.store(in: &cancellables) - } - - func reloadPersistenceObjects(persistenceManager: PersistenceManager) { - let vpnPersistence = persistenceManager.loadVPNPersistence( - withName: Constants.Persistence.profilesContainerName - ) - profileManager.swapProfileRepository(vpnPersistence.profileRepository()) - } -} diff --git a/Passepartout/App/Credits.rtf b/Passepartout/App/Credits.rtf deleted file mode 100644 index db1a5abe..00000000 --- a/Passepartout/App/Credits.rtf +++ /dev/null @@ -1,14 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2638 -\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;} -{\colortbl;\red255\green255\blue255;\red0\green0\blue233;} -{\*\expandedcolortbl;;\cssrgb\c0\c0\c93333;} -\paperw11900\paperh16840\margl1440\margr1440\vieww13440\viewh7800\viewkind0 -\deftab720 -\pard\pardeftab720\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "https://github.com/passepartoutvpn/passepartout-apple/blob/master/README.md"}}{\fldrslt -\f0\fs24 \cf2 \expnd0\expndtw0\kerning0 -\ul \ulc2 README}} -\f0\fs24 \expnd0\expndtw0\kerning0 -\'a0\'b7\'a0{\field{\*\fldinst{HYPERLINK "https://github.com/passepartoutvpn/passepartout-apple/blob/master/CHANGELOG.md"}}{\fldrslt \cf2 \ul \ulc2 CHANGELOG}}\'a0\'b7\'a0{\field{\*\fldinst{HYPERLINK "https://passepartoutvpn.app/faq/"}}{\fldrslt \cf2 \ul \ulc2 FAQ}}\ -\pard\pardeftab720\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "https://passepartoutvpn.app/disclaimer/"}}{\fldrslt \cf2 \ul \ulc2 Disclaimer}}\'a0\'b7\'a0{\field{\*\fldinst{HYPERLINK "https://passepartoutvpn.app/privacy/"}}{\fldrslt \cf2 \ul \ulc2 Privacy policy}}} \ No newline at end of file diff --git a/Passepartout/App/Domain/AppError.swift b/Passepartout/App/Domain/AppError.swift deleted file mode 100644 index fa17d25f..00000000 --- a/Passepartout/App/Domain/AppError.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// AppError.swift -// Passepartout -// -// Created by Davide De Rosa on 5/30/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -enum AppError: Error { - case profile(Passepartout.ProfileError) - - case provider(Passepartout.ProviderError) - - case vpn(Passepartout.VPNError) - - case tunnel(TunnelError) - - case generic(Error) - - init(_ error: Error) { - if let profileError = error as? Passepartout.ProfileError { - self = .profile(profileError) - } else if let providerError = error as? Passepartout.ProviderError { - self = .provider(providerError) - } else if let vpnError = error as? Passepartout.VPNError { - self = .vpn(vpnError) - } else { - self = .generic(error) - } - } -} diff --git a/Passepartout/App/Domain/Base.lproj/Intents.intentdefinition b/Passepartout/App/Domain/Base.lproj/Intents.intentdefinition deleted file mode 100644 index 53383964..00000000 --- a/Passepartout/App/Domain/Base.lproj/Intents.intentdefinition +++ /dev/null @@ -1,515 +0,0 @@ - - - - - INEnums - - INIntentDefinitionModelVersion - 1.2 - INIntentDefinitionNamespace - CM6KGi - INIntentDefinitionSystemVersion - 21E230 - INIntentDefinitionToolsBuildVersion - 13E113 - INIntentDefinitionToolsVersion - 13.3 - INIntents - - - INIntentCategory - generic - INIntentDescription - Connects to a host profile - INIntentDescriptionID - eXXb2z - INIntentLastParameterTag - 6 - INIntentName - ConnectVPN - INIntentParameterCombinations - - profileId,profileName - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Connect to ${profileName} - INIntentParameterCombinationTitleID - U6o81V - - - INIntentParameters - - - INIntentParameterDisplayPriority - 1 - INIntentParameterMetadata - - INIntentParameterMetadataCapitalization - Sentences - - INIntentParameterName - profileId - INIntentParameterTag - 4 - INIntentParameterType - String - - - INIntentParameterDisplayPriority - 2 - INIntentParameterMetadata - - INIntentParameterMetadataCapitalization - Sentences - - INIntentParameterName - profileName - INIntentParameterTag - 6 - INIntentParameterType - String - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Connect to VPN - INIntentTitleID - LA99yM - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - generic - INIntentDescription - Adds current Wi-Fi to trusted networks - INIntentDescriptionID - BKxs8X - INIntentName - TrustCurrentNetwork - INIntentParameterCombinations - - - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Trust current Wi-Fi - INIntentParameterCombinationTitleID - POyDPM - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Trust current Wi-Fi - INIntentTitleID - m2E7SI - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - generic - INIntentDescription - Disables the VPN service - INIntentDescriptionID - eQ1yzr - INIntentName - DisableVPN - INIntentParameterCombinations - - - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Disable VPN - INIntentParameterCombinationTitleID - IeGsEq - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Disable VPN - INIntentTitleID - 1ZRTCZ - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - generic - INIntentDescription - Removes current Wi-Fi from trusted networks - INIntentDescriptionID - 7eoAss - INIntentName - UntrustCurrentNetwork - INIntentParameterCombinations - - - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Untrust current Wi-Fi - INIntentParameterCombinationTitleID - 0Wu9nb - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Untrust current Wi-Fi - INIntentTitleID - rd1T8p - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - generic - INIntentDescription - Adds cellular to trusted networks - INIntentDescriptionID - 9GpJt5 - INIntentName - TrustCellularNetwork - INIntentParameterCombinations - - - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Trust cellular network - INIntentParameterCombinationTitleID - NWWgCl - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Trust cellular network - INIntentTitleID - H4taev - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - generic - INIntentDescription - Removes cellular from trusted networks - INIntentDescriptionID - 0jRWn5 - INIntentName - UntrustCellularNetwork - INIntentParameterCombinations - - - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Untrust cellular network - INIntentParameterCombinationTitleID - ggzKA2 - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Untrust cellular network - INIntentTitleID - wB1iYX - INIntentType - Custom - INIntentVerb - Do - - - INIntentCategory - generic - INIntentDescription - Connects to a specific location of a provider profile - INIntentDescriptionID - KjkCfU - INIntentLastParameterTag - 4 - INIntentName - MoveToLocation - INIntentParameterCombinations - - serverName,providerFullName,serverId,profileId - - INIntentParameterCombinationSubtitle - With ${providerFullName} provider - INIntentParameterCombinationSubtitleID - OeVNIO - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Connect to ${serverName} - INIntentParameterCombinationTitleID - nzeF6m - - - INIntentParameters - - - INIntentParameterDisplayPriority - 1 - INIntentParameterMetadata - - INIntentParameterMetadataCapitalization - Sentences - - INIntentParameterName - profileId - INIntentParameterTag - 2 - INIntentParameterType - String - - - INIntentParameterDisplayPriority - 2 - INIntentParameterMetadata - - INIntentParameterMetadataCapitalization - Sentences - INIntentParameterMetadataDefaultValueID - Cf6h1r - - INIntentParameterName - providerFullName - INIntentParameterTag - 4 - INIntentParameterType - String - - - INIntentParameterDisplayPriority - 3 - INIntentParameterMetadata - - INIntentParameterMetadataCapitalization - Sentences - - INIntentParameterName - serverId - INIntentParameterTag - 3 - INIntentParameterType - String - - - INIntentParameterDisplayPriority - 4 - INIntentParameterMetadata - - INIntentParameterMetadataCapitalization - Sentences - - INIntentParameterName - serverName - INIntentParameterTag - 1 - INIntentParameterType - String - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Connect to provider location - INIntentTitleID - qo3Szz - INIntentType - Custom - INIntentVerb - Go - - - INIntentCategory - generic - INIntentDescription - Enables the VPN service with the profile currently in use - INIntentDescriptionID - xY97Vu - INIntentName - EnableVPN - INIntentParameterCombinations - - - - INIntentParameterCombinationIsPrimary - - INIntentParameterCombinationSubtitle - With profile in use - INIntentParameterCombinationSubtitleID - NCoK9B - INIntentParameterCombinationSupportsBackgroundExecution - - INIntentParameterCombinationTitle - Enable VPN - INIntentParameterCombinationTitleID - yesvFP - - - INIntentResponse - - INIntentResponseCodes - - - INIntentResponseCodeName - success - INIntentResponseCodeSuccess - - - - INIntentResponseCodeName - failure - - - - INIntentTitle - Enable VPN - INIntentTitleID - lQ6ziK - INIntentType - Custom - INIntentVerb - Do - - - INTypes - - - diff --git a/Passepartout/App/Domain/IntentDispatcher+Activities.swift b/Passepartout/App/Domain/IntentDispatcher+Activities.swift deleted file mode 100644 index 2a239888..00000000 --- a/Passepartout/App/Domain/IntentDispatcher+Activities.swift +++ /dev/null @@ -1,161 +0,0 @@ -// -// IntentDispatcher+Activities.swift -// Passepartout -// -// Created by Davide De Rosa on 3/30/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Foundation -import Intents -import PassepartoutLibrary - -@MainActor -extension IntentDispatcher { - typealias VPNIntentActivity = IntentActivity - - static let enableVPN = VPNIntentActivity(name: Constants.Activities.enableVPN) { _, vpnManager in - pp_log.info("Enabling VPN...") - - Task { - do { - try await vpnManager.connectWithActiveProfile(toServer: nil) - } catch { - pp_log.error("Unable to connect with active profile: \(error)") - ErrorHandler.shared.handle(error) - } - } - } - - static let disableVPN = VPNIntentActivity(name: Constants.Activities.disableVPN) { _, vpnManager in - pp_log.info("Disabling VPN...") - - Task { - await vpnManager.disable() - } - } - - static let connectVPN = VPNIntentActivity(name: Constants.Activities.connectVPN) { activity, vpnManager in - pp_log.info("Connecting VPN...") - - guard let intent = activity.interaction?.intent as? ConnectVPNIntent else { - assertionFailure("Not a ConnectVPNIntent?") - return - } - guard let uuid = intent.profileId, let profileId = UUID(uuidString: uuid) else { - assertionFailure("Profile id is not valid") - if let interactionIdentifier = activity.interaction?.identifier { - INInteraction.delete(with: [interactionIdentifier], completion: nil) - } - return - } - Task { - do { - _ = try await vpnManager.connect(with: profileId) - } catch { - pp_log.error("Unable to connect with profile \(profileId): \(error)") - ErrorHandler.shared.handle(error) - } - } - } - - static let moveToLocation = VPNIntentActivity(name: Constants.Activities.moveToLocation) { activity, vpnManager in - pp_log.info("Moving to VPN location...") - - guard let intent = activity.interaction?.intent as? MoveToLocationIntent else { - assertionFailure("Not a MoveToLocationIntent?") - return - } - guard let uuid = intent.profileId, let profileId = UUID(uuidString: uuid) else { - if let interactionIdentifier = activity.interaction?.identifier { - INInteraction.delete(with: [interactionIdentifier], completion: nil) - } - return - } - guard let newServerId = intent.serverId else { - assertionFailure("Missing serverId") - if let interactionIdentifier = activity.interaction?.identifier { - INInteraction.delete(with: [interactionIdentifier], completion: nil) - } - return - } - Task { - do { - _ = try await vpnManager.connect(with: profileId, toServer: newServerId) - } catch { - pp_log.error("Unable to connect with profile \(profileId): \(error)") - ErrorHandler.shared.handle(error) - } - } - } - - static let trustCellularNetwork = VPNIntentActivity(name: Constants.Activities.trustCellularNetwork) { _, vpnManager in - pp_log.info("Trusting mobile network...") - handleCellularNetwork(true, vpnManager) - } - - static let trustCurrentNetwork = VPNIntentActivity(name: Constants.Activities.trustCurrentNetwork) { _, vpnManager in - pp_log.info("Trusting current Wi-Fi...") - handleCurrentNetwork(true, vpnManager) - } - - static let untrustCellularNetwork = VPNIntentActivity(name: Constants.Activities.untrustCellularNetwork) { _, vpnManager in - pp_log.info("Untrusting mobile network...") - handleCellularNetwork(false, vpnManager) - } - - static let untrustCurrentNetwork = VPNIntentActivity(name: Constants.Activities.untrustCurrentNetwork) { _, vpnManager in - pp_log.info("Untrusting current Wi-Fi...") - handleCurrentNetwork(false, vpnManager) - } - - private static func handleCellularNetwork(_ trust: Bool, _ vpnManager: VPNManager) { - Task { - do { - try await vpnManager.modifyActiveProfile { - $0.onDemand.withMobileNetwork = trust - } - } catch { - pp_log.error("Unable to modify cellular trust: \(error)") - ErrorHandler.shared.handle(error) - } - } - } - - private static func handleCurrentNetwork(_ trust: Bool, _ vpnManager: VPNManager) { - Task { - guard let ssid = await Utils.currentWifiSSID() else { - pp_log.warning("Not connected to any Wi-Fi or no permission to read location (needs 'While Using' or 'Always')") - return - } - do { - try await vpnManager.modifyActiveProfile { - pp_log.info("Wi-Fi SSID: \(ssid)") - $0.onDemand.withSSIDs[ssid] = trust - } - } catch { - pp_log.error("Unable to modify Wi-Fi trust: \(error)") - ErrorHandler.shared.handle(error) - } - } - } -} -#endif diff --git a/Passepartout/App/Domain/IntentDispatcher.swift b/Passepartout/App/Domain/IntentDispatcher.swift deleted file mode 100644 index a6e90d56..00000000 --- a/Passepartout/App/Domain/IntentDispatcher.swift +++ /dev/null @@ -1,161 +0,0 @@ -// -// IntentDispatcher.swift -// Passepartout -// -// Created by Davide De Rosa on 3/8/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Foundation -import Intents -import PassepartoutLibrary - -@MainActor -final class IntentDispatcher { - private struct Groups { - static let vpn = "VPN" - - static let trust = "Trust" - } - - // MARK: Intents - - static func intentConnect(header: Profile.Header) -> ConnectVPNIntent { - let intent = ConnectVPNIntent() - intent.profileId = header.id.uuidString - intent.profileName = header.name - return intent - } - - static func intentMoveTo(header: Profile.Header, providerFullName: String, server: ProviderServer) -> MoveToLocationIntent { - let intent = MoveToLocationIntent() - intent.profileId = header.id.uuidString - intent.providerFullName = providerFullName - intent.serverId = server.id - intent.serverName = server.localizedDescription(style: .longWithCategory(withCategory: false)) - return intent - } - - static func intentEnable() -> EnableVPNIntent { - EnableVPNIntent() - } - - static func intentDisable() -> DisableVPNIntent { - DisableVPNIntent() - } - - static func intentTrustWiFi() -> TrustCurrentNetworkIntent { - TrustCurrentNetworkIntent() - } - - static func intentUntrustWiFi() -> UntrustCurrentNetworkIntent { - UntrustCurrentNetworkIntent() - } - - static func intentTrustCellular() -> TrustCellularNetworkIntent { - TrustCellularNetworkIntent() - } - - static func intentUntrustCellular() -> UntrustCellularNetworkIntent { - UntrustCellularNetworkIntent() - } - - // MARK: Donations - - static func donateConnection(with profile: Profile, providerManager: ProviderManager) { - let genericIntent: INIntent - if let providerName = profile.header.providerName { - guard let provider = providerManager.provider(withName: providerName) else { - pp_log.warning("Intent provider not found") - return - } - guard let server = profile.providerServer(providerManager) else { - pp_log.warning("Intent server not found") - return - } - genericIntent = intentMoveTo(header: profile.header, providerFullName: provider.fullName, server: server) - } else { - genericIntent = intentConnect(header: profile.header) - } - - let interaction = INInteraction(intent: genericIntent, response: nil) - interaction.groupIdentifier = profile.id.uuidString - interaction.donateAndLog() - } - - static func donateEnableVPN() { - let interaction = INInteraction(intent: intentEnable(), response: nil) - interaction.groupIdentifier = Groups.vpn - interaction.donateAndLog() - } - - static func donateDisableVPN() { - let interaction = INInteraction(intent: intentDisable(), response: nil) - interaction.groupIdentifier = Groups.vpn - interaction.donateAndLog() - } - - static func donateTrustCurrentNetwork() { - let interaction = INInteraction(intent: intentTrustWiFi(), response: nil) - interaction.groupIdentifier = Groups.trust - interaction.donateAndLog() - } - - static func donateUntrustCurrentNetwork() { - let interaction = INInteraction(intent: intentUntrustWiFi(), response: nil) - interaction.groupIdentifier = Groups.trust - interaction.donateAndLog() - } - - static func donateTrustCellularNetwork() { - let interaction = INInteraction(intent: intentTrustCellular(), response: nil) - interaction.groupIdentifier = Groups.trust - interaction.donateAndLog() - } - - static func donateUntrustCellularNetwork() { - let interaction = INInteraction(intent: intentUntrustCellular(), response: nil) - interaction.groupIdentifier = Groups.trust - interaction.donateAndLog() - } - - static func forgetProfile(withHeader header: Profile.Header) { - INInteraction.delete(with: header.id.uuidString) { (error) in - if let error = error { - pp_log.warning("Unable to forget interactions: \(error)") - return - } - pp_log.debug("Removed profile \(header.name) interactions") - } - } -} - -private extension INInteraction { - func donateAndLog() { - donate { (error) in - if let error = error { - pp_log.error("Unable to donate interaction: \(error)") - } - pp_log.debug("Donated \(self.intent)") - } - } -} -#endif diff --git a/Passepartout/App/Domain/Picker+Network.swift b/Passepartout/App/Domain/Picker+Network.swift deleted file mode 100644 index 96dbdd8d..00000000 --- a/Passepartout/App/Domain/Picker+Network.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// Picker+Network.swift -// Passepartout -// -// Created by Davide De Rosa on 2/26/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary -import TunnelKitCore - -extension Network.DNSSettings { - static func availableConfigurationTypes(forVPNProtocol vpnProtocol: VPNProtocolType) -> [ConfigurationType] { - switch vpnProtocol { - case .openVPN: - return [.plain, .https, .tls, .disabled] - - case .wireGuard: - return [.plain, .https, .tls, .disabled] - } - } -} - -extension Network.ProxySettings { - static let availableConfigurationTypes: [ConfigurationType] = [ - .manual, - .pac, - .disabled - ] -} - -extension Network.MTUSettings { - static let availableBytes: [Int] = [0, 1500, 1400, 1300, 1200] -} diff --git a/Passepartout/App/Domain/Picker+OpenVPN.swift b/Passepartout/App/Domain/Picker+OpenVPN.swift deleted file mode 100644 index 3bbb49a8..00000000 --- a/Passepartout/App/Domain/Picker+OpenVPN.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// Picker+OpenVPN.swift -// Passepartout -// -// Created by Davide De Rosa on 6/22/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import TunnelKitOpenVPN - -extension OpenVPN.Cipher { - static let available: [OpenVPN.Cipher] = [ - .aes256gcm, - .aes192gcm, - .aes128gcm, - .aes256cbc, - .aes192cbc, - .aes128cbc - ] -} - -extension OpenVPN.Digest { - static let available: [OpenVPN.Digest] = [ - .sha1, - .sha224, - .sha256, - .sha384, - .sha512 - ] -} - -extension OpenVPN.CompressionFraming { - static let available: [OpenVPN.CompressionFraming] = [ - .disabled, - .compLZO, - .compress - ] -} - -extension OpenVPN.CompressionAlgorithm { - static let available: [OpenVPN.CompressionAlgorithm] = [ - .disabled, - .LZO - ] -} diff --git a/Passepartout/App/Domain/de.lproj/Intents.strings b/Passepartout/App/Domain/de.lproj/Intents.strings deleted file mode 100644 index ae672575..00000000 --- a/Passepartout/App/Domain/de.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Entfernt Mobilfunknetz von vertrauten Netzwerken"; - -"IeGsEq" = "VPN deaktivieren"; -"1ZRTCZ" = "VPN deaktivieren"; - -"66bZBE" = "Mit Anbieter ${providerFullName}"; - -"7eoAss" = "Entferne aktuelles WLAN von vertrauten Netzwerken"; - -"9GpJt5" = "Fügt Mobilnetz zu vertrauten Netzwerken hinzu"; - -"BKxs8X" = "Fügt aktuelles WLAN zu vertrauten Netzwerken hinzu"; - -"NWWgCl" = "Mobilfunknetz vertrauen"; -"H4taev" = "Mobilfunknetz vertrauen"; - -"KjkCfU" = "Connects to a specific location of a provider profile"; - -"LA99yM" = "Verbinde mit VPN"; - -"U6o81V" = "Verbinde mit ${profileName}"; - -"WnTPFg" = "Verbinde mit ${serverName}"; - -"eQ1yzr" = "Deaktiviert den VPN-Dienst"; - -"eXXb2z" = "Verbindet mit einem Hostprofil"; - -"yesvFP" = "Aktiviere VPN"; -"lQ6ziK" = "Aktiviere VPN"; - -"POyDPM" = "Vertraue aktuellem WLAN"; -"m2E7SI" = "Vertraue aktuellem WLAN"; - -"qo3Szz" = "Verbinde mit Anbieter-Ort"; - -"0Wu9nb" = "Aktuellem WLAN nicht vertrauen"; -"rd1T8p" = "Aktuellem WLAN nicht vertrauen"; - -"ggzKA2" = "Mobilfunknetz nicht vertrauen"; -"wB1iYX" = "Mobilfunknetz nicht vertrauen"; - -"xY97Vu" = "Aktiviert den VPN-Dienst mit dem derzeitig benutzten Profil"; - -"NCoK9B" = "Mit dem benutzten Profil"; diff --git a/Passepartout/App/Domain/el.lproj/Intents.strings b/Passepartout/App/Domain/el.lproj/Intents.strings deleted file mode 100644 index e67fbc67..00000000 --- a/Passepartout/App/Domain/el.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Αφαιρεί το κινητό δίκτυο από τα αξιόπιστα δίκτυα"; - -"IeGsEq" = "Απενεργοποίηση VPN"; -"1ZRTCZ" = "Απενεργοποίηση VPN"; - -"66bZBE" = "Με ${providerFullName} πάροχο"; - -"7eoAss" = "Αφαίρεση συνδεδεμένου Wi-Fi από τα έμπιστα δίκτυα"; - -"9GpJt5" = "Προσθέτει το κινητό δίκτυο στα αξιόπιστα δίκτυα"; - -"BKxs8X" = "Προσθήκη τρέχων Wi-Fi στα έμπιστα δίκτυα"; - -"NWWgCl" = "Αξιόπιστο Δίκτυο κινητής τηλεφωνίας"; -"H4taev" = "Αξιόπιστο Δίκτυο κινητής τηλεφωνίας"; - -"KjkCfU" = "Συνδέετε σε μια συγκεκριμένη τοποθεσία ενός προφίλ παρόχου"; - -"LA99yM" = "Σύνδεση VPN"; - -"U6o81V" = "Σύνδεση στο ${profileName}"; - -"WnTPFg" = "Σύνδεση σε ${serverName}"; - -"eQ1yzr" = "Απενεργοποίηση υπηρεσίας VPN"; - -"eXXb2z" = "Συνδέεται σε ένα προφίλ διακομιστή"; - -"yesvFP" = "Ενεργοποίηση VPN"; -"lQ6ziK" = "Ενεργοποίηση VPN"; - -"POyDPM" = "Εμπιστευθείτε το τρέχον Wi-Fi"; -"m2E7SI" = "Εμπιστευθείτε το τρέχον Wi-Fi"; - -"qo3Szz" = "Συνδεθείτε με τη θέση του παρόχου"; - -"0Wu9nb" = "Μην εμπιστευθείτε το τρέχον Wi-Fi"; -"rd1T8p" = "Μην εμπιστευθείτε το τρέχον Wi-Fi"; - -"ggzKA2" = "Μη αξιόπιστο κινητό δίκτυο"; -"wB1iYX" = "Μη αξιόπιστο κινητό δίκτυο"; - -"xY97Vu" = "Ενεργοποιεί την υπηρεσία VPN με το προφίλ που χρησιμοποιείται αυτήν τη στιγμή"; - -"NCoK9B" = "Με προφίλ σε χρήση"; diff --git a/Passepartout/App/Domain/en.lproj/Intents.strings b/Passepartout/App/Domain/en.lproj/Intents.strings deleted file mode 100644 index 0a13a9cf..00000000 --- a/Passepartout/App/Domain/en.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Removes cellular from trusted networks"; - -"IeGsEq" = "Disable VPN"; -"1ZRTCZ" = "Disable VPN"; - -"66bZBE" = "With ${providerFullName} provider"; - -"7eoAss" = "Removes current Wi-Fi from trusted networks"; - -"9GpJt5" = "Adds cellular to trusted networks"; - -"BKxs8X" = "Adds current Wi-Fi to trusted networks"; - -"NWWgCl" = "Trust cellular network"; -"H4taev" = "Trust cellular network"; - -"KjkCfU" = "Connects to a specific location of a provider profile"; - -"LA99yM" = "Connect to VPN"; - -"U6o81V" = "Connect to ${profileName}"; - -"WnTPFg" = "Connect to ${serverName}"; - -"eQ1yzr" = "Disables the VPN service"; - -"eXXb2z" = "Connects to a host profile"; - -"yesvFP" = "Enable VPN"; -"lQ6ziK" = "Enable VPN"; - -"POyDPM" = "Trust current Wi-Fi"; -"m2E7SI" = "Trust current Wi-Fi"; - -"qo3Szz" = "Connect to provider location"; - -"0Wu9nb" = "Untrust current Wi-Fi"; -"rd1T8p" = "Untrust current Wi-Fi"; - -"ggzKA2" = "Untrust cellular network"; -"wB1iYX" = "Untrust cellular network"; - -"xY97Vu" = "Enables the VPN service with the profile currently in use"; - -"NCoK9B" = "With profile in use"; diff --git a/Passepartout/App/Domain/es.lproj/Intents.strings b/Passepartout/App/Domain/es.lproj/Intents.strings deleted file mode 100644 index e38bd40e..00000000 --- a/Passepartout/App/Domain/es.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Borra la red móvil de las redes de confianza"; - -"IeGsEq" = "Deshabilitar VPN"; -"1ZRTCZ" = "Deshabilitar VPN"; - -"66bZBE" = "Con el proveedor ${providerFullName}"; - -"7eoAss" = "Borra el Wi-Fi en uso de las redes de confianza"; - -"9GpJt5" = "Añade la red móvil a las redes de confianza"; - -"BKxs8X" = "Añade el Wi-Fi en uso a las redes de confianza"; - -"NWWgCl" = "Añadir red móvil de confianza"; -"H4taev" = "Añadir red móvil de confianza"; - -"KjkCfU" = "Conecta con una región específica de un proveedor"; - -"LA99yM" = "Conectar con el VPN"; - -"U6o81V" = "Conectar con ${profileName}"; - -"WnTPFg" = "Conectar con ${serverName}"; - -"eQ1yzr" = "Deshabilita el servicio VPN"; - -"eXXb2z" = "Conecta con un host"; - -"yesvFP" = "Habilitar VPN"; -"lQ6ziK" = "Habilitar VPN"; - -"POyDPM" = "Añadir Wi-Fi de confianza"; -"m2E7SI" = "Añadir Wi-Fi de confianza"; - -"qo3Szz" = "Conectar con una región del proveedor"; - -"0Wu9nb" = "Borrar Wi-Fi de confianza"; -"rd1T8p" = "Borrar Wi-Fi de confianza"; - -"ggzKA2" = "Borrar red móvil de confianza"; -"wB1iYX" = "Borrar red móvil de confianza"; - -"xY97Vu" = "Habilita el servicio VPN con el perfil en uso"; - -"NCoK9B" = "Con el perfil en uso"; diff --git a/Passepartout/App/Domain/fr.lproj/Intents.strings b/Passepartout/App/Domain/fr.lproj/Intents.strings deleted file mode 100755 index 8e5af442..00000000 --- a/Passepartout/App/Domain/fr.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Supprime le réseau cellulaire des réseaux de confiance"; - -"IeGsEq" = "Désactive VPN"; -"1ZRTCZ" = "Désactive VPN"; - -"66bZBE" = "Avec ${providerFullName} fournisseur"; - -"7eoAss" = "Supprime le présent réseaux Wi-Fi des réseaux de confiance "; - -"9GpJt5" = "Ajoutes le réseau cellulaire aux réseaux de confiance"; - -"BKxs8X" = "Ajoutes le présent réseau Wi-Fi aux réseaux de confiance"; - -"NWWgCl" = "Faire confiance au réseau cellulaire"; -"H4taev" = "Faire confiance au réseau cellulaire"; - -"KjkCfU" = "Connecter à une localization spécifique d'un profile de fournisseur"; - -"LA99yM" = "Se connecter au VPN"; - -"U6o81V" = "Se connecter à ${profileName}"; - -"WnTPFg" = "Se connecter à ${serverName}"; - -"eQ1yzr" = "Désactives le service VPN"; - -"eXXb2z" = "Connectes à un profile hôte"; - -"yesvFP" = "Activer VPN"; -"lQ6ziK" = "Activer VPN"; - -"POyDPM" = "Faire confiance au présent réseau Wi-Fi"; -"m2E7SI" = "Faire confiance au présent réseau Wi-Fi"; - -"qo3Szz" = "Se connecter à la localisation du fournisseur"; - -"0Wu9nb" = "Ne plus faire confiance au présent réseau Wi-Fi"; -"rd1T8p" = "Ne plus faire confiance au présent réseau Wi-Fi"; - -"ggzKA2" = "Faire confiance au présent réseau cellulaire"; -"wB1iYX" = "Faire confiance au présent réseau cellulaire"; - -"xY97Vu" = "Activer le service VPN avec le profile présentement utilisé"; - -"NCoK9B" = "Avec le profile utilisé"; diff --git a/Passepartout/App/Domain/it.lproj/Intents.strings b/Passepartout/App/Domain/it.lproj/Intents.strings deleted file mode 100644 index 4748e6ef..00000000 --- a/Passepartout/App/Domain/it.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Rimuove la rete mobile dalle reti sicure"; - -"IeGsEq" = "Disabilita VPN"; -"1ZRTCZ" = "Disabilita VPN"; - -"66bZBE" = "Con il provider ${providerFullName}"; - -"7eoAss" = "Rimuove la Wi-Fi corrente dalle reti sicure"; - -"9GpJt5" = "Aggiunge la rete mobile alle reti sicure"; - -"BKxs8X" = "Aggiunge la Wi-Fi corrente alle reti sicure"; - -"NWWgCl" = "Aggiungi rete mobile sicura"; -"H4taev" = "Aggiungi rete mobile sicura"; - -"KjkCfU" = "Avvia una connessione ad una regione specifica di un provider"; - -"LA99yM" = "Connetti alla VPN"; - -"U6o81V" = "Connettiti a ${profileName}"; - -"WnTPFg" = "Connettiti in ${serverName}"; - -"eQ1yzr" = "Disabilita il servizio VPN"; - -"eXXb2z" = "Avvia la connessione ad un host"; - -"yesvFP" = "Abilita VPN"; -"lQ6ziK" = "Abilita VPN"; - -"POyDPM" = "Aggiungi Wi-Fi sicura"; -"m2E7SI" = "Aggiungi Wi-Fi sicura"; - -"qo3Szz" = "Connettiti a una regione del provider"; - -"0Wu9nb" = "Rimuovi Wi-Fi sicura"; -"rd1T8p" = "Rimuovi Wi-Fi sicura"; - -"ggzKA2" = "Rimuovi rete mobile sicura"; -"wB1iYX" = "Rimuovi rete mobile sicura"; - -"xY97Vu" = "Abilita il servizio VPN con il profilo attualmente in uso"; - -"NCoK9B" = "Con il profilo in uso"; diff --git a/Passepartout/App/Domain/nl.lproj/Intents.strings b/Passepartout/App/Domain/nl.lproj/Intents.strings deleted file mode 100644 index 174b79a3..00000000 --- a/Passepartout/App/Domain/nl.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Verwijder Mobielnetwerk van vertrouwde netwerken"; - -"IeGsEq" = "Disable VPN"; -"1ZRTCZ" = "Disable VPN"; - -"66bZBE" = "Met ${providerFullName} aanbieder"; - -"7eoAss" = "Verwijder huidige Wi-Fi van vertrouwde netwerken"; - -"9GpJt5" = "Voeg Mobielnetwerk to aan vertrouwde netwerken"; - -"BKxs8X" = "Voeg huidig Wi-Fi toe aan vertrouwde netwerken"; - -"NWWgCl" = "Vertrouw mobiel netwerk"; -"H4taev" = "Vertrouw mobiel netwerk"; - -"KjkCfU" = "Maak verbinding met een specifieke lokatie van een aanbieder profiel"; - -"LA99yM" = "Verbind VPN"; - -"U6o81V" = "Verbind met ${profileName}"; - -"WnTPFg" = "Verbind met ${serverName}"; - -"eQ1yzr" = "Schakel VPN service uit"; - -"eXXb2z" = "Verbind met een host profiel"; - -"yesvFP" = "Schakel VPN in"; -"lQ6ziK" = "Schakel VPN in"; - -"POyDPM" = "Vertrouw huidig Wi-Fi netwerk"; -"m2E7SI" = "Vertrouw huidig Wi-Fi netwerk"; - -"qo3Szz" = "Maak verbinding met de locatie van de aanbieder"; - -"0Wu9nb" = "Wantrouw huidig Wi-Fi netwerk"; -"rd1T8p" = "Wantrouw huidig Wi-Fi netwerk"; - -"ggzKA2" = "Wantrouw modbiel netwerk"; -"wB1iYX" = "Wantrouw modbiel netwerk"; - -"xY97Vu" = "Schakel de VPN-service in met het profiel dat momenteel in gebruik is"; - -"NCoK9B" = "Met het profiel dat momenteel in gebruik is"; diff --git a/Passepartout/App/Domain/pl.lproj/Intents.strings b/Passepartout/App/Domain/pl.lproj/Intents.strings deleted file mode 100644 index ddb5aae7..00000000 --- a/Passepartout/App/Domain/pl.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Usuwa dane komórkowe z zaufanych sieci"; - -"IeGsEq" = "Wyłącz VPN"; -"1ZRTCZ" = "Wyłącz VPN"; - -"66bZBE" = "Z usługodawcą ${providerFullName}"; - -"7eoAss" = "Usuwa obecnie połączoną sieć Wi-Fi z zaufanych sieci"; - -"9GpJt5" = "Dodaje dane komórkowe do zaufanych sieci"; - -"BKxs8X" = "Dodaje obecnie połączoną sieć Wi-Fi do zaufanych sieci"; - -"NWWgCl" = "Ufaj danym komórkowym"; -"H4taev" = "Ufaj danym komórkowym"; - -"KjkCfU" = "Łączy z wybraną lokalizacją profilu usługodawcy"; - -"LA99yM" = "Połącz z VPN"; - -"U6o81V" = "Połącz z ${profileName}"; - -"WnTPFg" = "Połącz z ${serverName}"; - -"eQ1yzr" = "Wyłącza VPN"; - -"eXXb2z" = "Łączy z profilem hosta"; - -"yesvFP" = "Włącz VPN"; -"lQ6ziK" = "Włącz VPN"; - -"POyDPM" = "Zaufaj obecnie połączonej sieci Wi-Fi"; -"m2E7SI" = "Zaufaj obecnie połączonej sieci Wi-Fi"; - -"qo3Szz" = "Połącz z lokalizacją usługodawcy"; - -"0Wu9nb" = "Nie ufaj obecnie połączonej sieci Wi-Fi"; -"rd1T8p" = "Nie ufaj obecnie połączonej sieci Wi-Fi"; - -"ggzKA2" = "Nie ufaj danym komórkowym"; -"wB1iYX" = "Nie ufaj danym komórkowym"; - -"xY97Vu" = "Włącza VPN używając wybranego obecnie profilu"; - -"NCoK9B" = "Z profilem w użyciu"; diff --git a/Passepartout/App/Domain/pt.lproj/Intents.strings b/Passepartout/App/Domain/pt.lproj/Intents.strings deleted file mode 100644 index a8ae762e..00000000 --- a/Passepartout/App/Domain/pt.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Remover celular de conexões seguras"; - -"IeGsEq" = "Desativar VPN"; -"1ZRTCZ" = "Desativar VPN"; - -"66bZBE" = "Com o provedor ${providerFullName}"; - -"7eoAss" = "Remover Wi-Fi atual de conexões seguras"; - -"9GpJt5" = "Adicionar celular em conexões seguras"; - -"BKxs8X" = "Adicionar Wi-Fi atual em conexões seguras"; - -"NWWgCl" = "Confiar em rede celular"; -"H4taev" = "Confiar em rede celular"; - -"KjkCfU" = "Conectar em uma localização específica de um provedor"; - -"LA99yM" = "Conectar VPN"; - -"U6o81V" = "Conectar ${profileName}"; - -"WnTPFg" = "Conectar ${serverName}"; - -"eQ1yzr" = "Desabilitar serviço de VPN"; - -"eXXb2z" = "Conectar em um host"; - -"yesvFP" = "Ativar VPN"; -"lQ6ziK" = "Ativar VPN"; - -"POyDPM" = "Confiar na Wi-Fi atual"; -"m2E7SI" = "Confiar na Wi-Fi atual"; - -"qo3Szz" = "Conectar em uma região do provedor"; - -"0Wu9nb" = "Não confiar na Wi-Fi atual"; -"rd1T8p" = "Não confiar na Wi-Fi atual"; - -"ggzKA2" = "Não confiar na conexão celular"; -"wB1iYX" = "Não confiar na conexão celular"; - -"xY97Vu" = "Ativar o serviço VPN no perfil em uso"; - -"NCoK9B" = "Com o perfil em uso"; diff --git a/Passepartout/App/Domain/ru.lproj/Intents.strings b/Passepartout/App/Domain/ru.lproj/Intents.strings deleted file mode 100644 index 6979c8ed..00000000 --- a/Passepartout/App/Domain/ru.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Удаляет мобильную сеть из доверенных подключений"; - -"IeGsEq" = "Отключить VPN"; -"1ZRTCZ" = "Отключить VPN"; - -"66bZBE" = "С ${providerFullName} провайдером"; - -"7eoAss" = "Удаляет текущий Wi-Fi из доверенных подключений"; - -"9GpJt5" = "Добавляет мобильную сеть в доверенные подключения"; - -"BKxs8X" = "Добавляет текущий  Wi-Fi в доверенные подключения"; - -"NWWgCl" = "Доверять мобильной сети"; -"H4taev" = "Доверять мобильной сети"; - -"KjkCfU" = "Подключиться к конкретному местоположению провайдера"; - -"LA99yM" = "Подключиться к VPN"; - -"U6o81V" = "Подключиться к ${profileName}"; - -"WnTPFg" = "Подключиться к ${serverName}"; - -"eQ1yzr" = "Отключить этот VPN сервис"; - -"eXXb2z" = "Подключается к хост профилю"; - -"yesvFP" = "Включить VPN"; -"lQ6ziK" = "Включить VPN"; - -"POyDPM" = "Доверять текущему Wi-Fi"; -"m2E7SI" = "Доверять текущему Wi-Fi"; - -"qo3Szz" = "Подключиться к местоположению провайдера"; - -"0Wu9nb" = "Не доверять текущему Wi-Fi"; -"rd1T8p" = "Не доверять текущему Wi-Fi"; - -"ggzKA2" = "Не доверять мобильной сети"; -"wB1iYX" = "Не доверять мобильной сети"; - -"xY97Vu" = "Включает VPN с используемым профилем"; - -"NCoK9B" = "С используемым профилем"; diff --git a/Passepartout/App/Domain/sv.lproj/Intents.strings b/Passepartout/App/Domain/sv.lproj/Intents.strings deleted file mode 100644 index 9feaa326..00000000 --- a/Passepartout/App/Domain/sv.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Tar bort cellular från betrodda nätverk"; - -"IeGsEq" = "Avstäng VPN"; -"1ZRTCZ" = "Avstäng VPN"; - -"66bZBE" = "Med ${providerFullName} leverantör"; - -"7eoAss" = "Tar bort nuvarande Wi-Fi från betrodda nätverk"; - -"9GpJt5" = "Tillsätter cellular till lita betrodda nätverk"; - -"BKxs8X" = "Tillsätter nuvarande Wi-Fi till betrodda nätverk"; - -"NWWgCl" = "Lita på cellular nätverk"; -"H4taev" = "Lita på cellular nätverk"; - -"KjkCfU" = "Koppla till en specifik plats från en leverantör profil"; - -"LA99yM" = "Koppla till VPN"; - -"U6o81V" = "Koppla till ${profileName}"; - -"WnTPFg" = "Koppla till ${serverName}"; - -"eQ1yzr" = "Avstäng VPN service"; - -"eXXb2z" = "Koppla till en host profil"; - -"yesvFP" = "Avstäng VPN"; -"lQ6ziK" = "Avstäng VPN"; - -"POyDPM" = "lita på närvarande Wi-Fi"; -"m2E7SI" = "lita på närvarande Wi-Fi"; - -"qo3Szz" = "Koppla till provider plats"; - -"0Wu9nb" = "Untrust närvarande Wi-Fi"; -"rd1T8p" = "Untrust närvarande Wi-Fi"; - -"ggzKA2" = "Untrust cellular nätverk"; -"wB1iYX" = "Untrust cellular nätverk"; - -"xY97Vu" = "På-sätter VPN service med närvarande profil"; - -"NCoK9B" = "Med profil i andvänding"; diff --git a/Passepartout/App/Domain/uk.lproj/Intents.strings b/Passepartout/App/Domain/uk.lproj/Intents.strings deleted file mode 100644 index 3dc7ecc4..00000000 --- a/Passepartout/App/Domain/uk.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "Видаляє мобільну мережу з довірених підключень"; - -"IeGsEq" = "Вимкнути VPN"; -"1ZRTCZ" = "Вимкнути VPN"; - -"66bZBE" = "З ${providerFullName} провайдером"; - -"7eoAss" = "Видаляє поточний Wi-Fi з довірених підключень"; - -"9GpJt5" = "Додає мобільну мережу у довірені підключеня"; - -"BKxs8X" = "Додає поточний Wi-Fi у довірені підключеня"; - -"NWWgCl" = "Довіряти мобільній мережі"; -"H4taev" = "Довіряти мобільній мережі"; - -"KjkCfU" = "Підключитися до конкретного розташування провайдеру"; - -"LA99yM" = "Підключитися до VPN"; - -"U6o81V" = "Підключитися до ${profileName}"; - -"WnTPFg" = "Підключитися до ${serverName}"; - -"eQ1yzr" = "Відключити цей VPN сервіс"; - -"eXXb2z" = "Підключитися до хост профілю"; - -"yesvFP" = "Увімкнути VPN"; -"lQ6ziK" = "Увімкнути VPN"; - -"POyDPM" = "Довіряти поточному Wi-Fi"; -"m2E7SI" = "Довіряти поточному Wi-Fi"; - -"qo3Szz" = "Підключитися до розташування провайдеру"; - -"0Wu9nb" = "Не довіряти поточному Wi-Fi"; -"rd1T8p" = "Не довіряти поточному Wi-Fi"; - -"ggzKA2" = "Не довіряти мобільній мережі"; -"wB1iYX" = "Не довіряти мобільній мережі"; - -"xY97Vu" = "Вмикає VPN з профілем, що використовується"; - -"NCoK9B" = "З профілем, що використовується"; diff --git a/Passepartout/App/Domain/zh-Hans.lproj/Intents.strings b/Passepartout/App/Domain/zh-Hans.lproj/Intents.strings deleted file mode 100644 index be6068f3..00000000 --- a/Passepartout/App/Domain/zh-Hans.lproj/Intents.strings +++ /dev/null @@ -1,45 +0,0 @@ -"0jRWn5" = "从可信网络中移除蜂窝网络"; - -"IeGsEq" = "禁用VPN"; -"1ZRTCZ" = "禁用VPN"; - -"66bZBE" = "使用 ${providerFullName} 提供商配置"; - -"7eoAss" = "从可信网络中移除当前Wi-Fi"; - -"9GpJt5" = "添加蜂窝网络到可信网络中"; - -"BKxs8X" = "添加当前Wi-Fi到可信网络中"; - -"NWWgCl" = "信任蜂窝网络"; -"H4taev" = "信任蜂窝网络"; - -"KjkCfU" = "连接到指定地区的提供商配置"; - -"LA99yM" = "连接到VPN"; - -"U6o81V" = "连接到${profileId}"; - -"WnTPFg" = "连接到${serverName}"; - -"eQ1yzr" = "禁用VPN服务"; - -"eXXb2z" = "连接到主机配置"; - -"yesvFP" = "启用VPN"; -"lQ6ziK" = "启用VPN"; - -"POyDPM" = "信任当前Wi-Fi"; -"m2E7SI" = "信任当前Wi-Fi"; - -"qo3Szz" = "连接到提供商的地区"; - -"0Wu9nb" = "不信任当前Wi-Fi"; -"rd1T8p" = "不信任当前Wi-Fi"; - -"ggzKA2" = "不信任当前蜂窝网络"; -"wB1iYX" = "不信任当前蜂窝网络"; - -"xY97Vu" = "使用当前配置启用VPN服务"; - -"NCoK9B" = "该配置在被使用"; diff --git a/Passepartout/App/Extensions/PassepartoutProviders+Extensions.swift b/Passepartout/App/Extensions/PassepartoutProviders+Extensions.swift deleted file mode 100644 index 9f68b695..00000000 --- a/Passepartout/App/Extensions/PassepartoutProviders+Extensions.swift +++ /dev/null @@ -1,123 +0,0 @@ -// -// PassepartoutProviders+Extensions.swift -// Passepartout -// -// Created by Davide De Rosa on 11/4/21. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -extension ProviderMetadata: Identifiable, Comparable, Hashable { - public var id: String { - name - } - - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.name == rhs.name - } - - public static func < (lhs: Self, rhs: Self) -> Bool { - lhs.fullName.lowercased() < rhs.fullName.lowercased() - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(name) - } -} - -extension ProviderCategory: Comparable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.name.lowercased() == rhs.name.lowercased() - } - - public static func < (lhs: Self, rhs: Self) -> Bool { - lhs.name.lowercased() < rhs.name.lowercased() - } -} - -extension ProviderLocation: Comparable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.countryCode == rhs.countryCode - } - - public static func < (lhs: Self, rhs: Self) -> Bool { - lhs.localizedDescription(style: .country) < rhs.localizedDescription(style: .country) - } -} - -extension ProviderServer: Comparable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.id == rhs.id - } - - // "Default" comes first (nil localizedName) - public static func < (lhs: Self, rhs: Self) -> Bool { - guard lhs.localizedName != rhs.localizedName else { - guard let li = lhs.serverIndex else { - return true - } - guard let ri = rhs.serverIndex else { - return false - } - guard li != ri else { - guard lhs.apiId != rhs.apiId else { - return lhs.tags?.joined() ?? "" < rhs.tags?.joined() ?? "" - } - return lhs.apiId < rhs.apiId - } - return li < ri - } - guard let ld = lhs.localizedName else { - return true - } - guard let rd = rhs.localizedName else { - return false - } - return ld < rd - } -} - -extension ProviderServer.Preset: Comparable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.name == rhs.name - } - - public static func < (lhs: Self, rhs: Self) -> Bool { - lhs.name < rhs.name - } -} - -extension ProviderMetadata { - var openVPNGuidanceURL: URL? { - guard let string = Constants.URLs.openVPNGuidances[name] else { - return nil - } - return URL(string: string) - } - - var referralURL: URL? { - guard let string = Constants.URLs.referrals[name] else { - return nil - } - return URL(string: string) - } -} diff --git a/Passepartout/App/Extensions/TunnelKit+Extensions.swift b/Passepartout/App/Extensions/TunnelKit+Extensions.swift deleted file mode 100644 index d56ed90e..00000000 --- a/Passepartout/App/Extensions/TunnelKit+Extensions.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// TunnelKit+Extensions.swift -// Passepartout -// -// Created by Davide De Rosa on 3/12/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import TunnelKitCore - -extension Endpoint: Identifiable { - public var id: String { - [address, proto.port.description, proto.socketType.rawValue].joined(separator: ":") - } -} - -extension Endpoint: Comparable { - public static func < (lhs: Self, rhs: Self) -> Bool { - guard lhs.address != rhs.address else { - return lhs.proto < rhs.proto - } - guard lhs.isHostname == rhs.isHostname else { - return lhs.isHostname - } - return lhs.address < rhs.address - } -} - -extension Endpoint: Hashable { -} - -extension EndpointProtocol: Comparable { - public static func < (lhs: Self, rhs: Self) -> Bool { - guard lhs.socketType != rhs.socketType else { - return lhs.port < rhs.port - } - return lhs.socketType.orderValue < rhs.socketType.orderValue - } -} - -private extension SocketType { - var orderValue: Int { - switch self { - case .udp: return 1 - case .udp4: return 2 - case .udp6: return 3 - case .tcp: return 4 - case .tcp4: return 5 - case .tcp6: return 6 - } - } -} - -extension IPv4Settings.Route: Identifiable { - public var id: String { - [destination, mask, gateway ?? "*"].joined(separator: ":") - } -} - -extension IPv6Settings.Route: Identifiable { - public var id: String { - [destination, prefixLength.description, gateway ?? "*"].joined(separator: ":") - } -} diff --git a/Passepartout/App/Flags.xcassets/Contents.json b/Passepartout/App/Flags.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/Passepartout/App/Flags.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Flags.xcassets/flags/Contents.json b/Passepartout/App/Flags.xcassets/flags/Contents.json deleted file mode 100644 index 6e965652..00000000 --- a/Passepartout/App/Flags.xcassets/flags/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/Passepartout/App/Flags.xcassets/flags/ad.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ad.imageset/Contents.json deleted file mode 100644 index a471458c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ad.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ad@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ad@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ad.imageset/ad@2x.png b/Passepartout/App/Flags.xcassets/flags/ad.imageset/ad@2x.png deleted file mode 100644 index 10dfa369..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ad.imageset/ad@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ad.imageset/ad@3x.png b/Passepartout/App/Flags.xcassets/flags/ad.imageset/ad@3x.png deleted file mode 100644 index 55182112..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ad.imageset/ad@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ae.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ae.imageset/Contents.json deleted file mode 100644 index 6f4b00ab..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ae.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ae@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ae@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ae.imageset/ae@2x.png b/Passepartout/App/Flags.xcassets/flags/ae.imageset/ae@2x.png deleted file mode 100644 index 8040d736..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ae.imageset/ae@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ae.imageset/ae@3x.png b/Passepartout/App/Flags.xcassets/flags/ae.imageset/ae@3x.png deleted file mode 100644 index 0f5d94b8..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ae.imageset/ae@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/af.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/af.imageset/Contents.json deleted file mode 100644 index 69e26fc4..00000000 --- a/Passepartout/App/Flags.xcassets/flags/af.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "af@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "af@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/af.imageset/af@2x.png b/Passepartout/App/Flags.xcassets/flags/af.imageset/af@2x.png deleted file mode 100644 index 1b96724f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/af.imageset/af@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/af.imageset/af@3x.png b/Passepartout/App/Flags.xcassets/flags/af.imageset/af@3x.png deleted file mode 100644 index 8e98490a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/af.imageset/af@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ag.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ag.imageset/Contents.json deleted file mode 100644 index 74d0de20..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ag.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ag@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ag@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ag.imageset/ag@2x.png b/Passepartout/App/Flags.xcassets/flags/ag.imageset/ag@2x.png deleted file mode 100644 index 8496e2ec..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ag.imageset/ag@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ag.imageset/ag@3x.png b/Passepartout/App/Flags.xcassets/flags/ag.imageset/ag@3x.png deleted file mode 100644 index 386ca199..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ag.imageset/ag@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ai.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ai.imageset/Contents.json deleted file mode 100644 index c16ebd2b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ai.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ai@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ai@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ai.imageset/ai@2x.png b/Passepartout/App/Flags.xcassets/flags/ai.imageset/ai@2x.png deleted file mode 100644 index 8d0c68b7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ai.imageset/ai@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ai.imageset/ai@3x.png b/Passepartout/App/Flags.xcassets/flags/ai.imageset/ai@3x.png deleted file mode 100644 index 37ed2148..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ai.imageset/ai@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/al.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/al.imageset/Contents.json deleted file mode 100644 index f895dcbd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/al.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "al@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "al@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/al.imageset/al@2x.png b/Passepartout/App/Flags.xcassets/flags/al.imageset/al@2x.png deleted file mode 100644 index 0a76419d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/al.imageset/al@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/al.imageset/al@3x.png b/Passepartout/App/Flags.xcassets/flags/al.imageset/al@3x.png deleted file mode 100644 index 9ee0100a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/al.imageset/al@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/am.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/am.imageset/Contents.json deleted file mode 100644 index 398dde0c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/am.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "am@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "am@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/am.imageset/am@2x.png b/Passepartout/App/Flags.xcassets/flags/am.imageset/am@2x.png deleted file mode 100644 index d704336e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/am.imageset/am@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/am.imageset/am@3x.png b/Passepartout/App/Flags.xcassets/flags/am.imageset/am@3x.png deleted file mode 100644 index f5d63415..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/am.imageset/am@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ao.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ao.imageset/Contents.json deleted file mode 100644 index b1d09b75..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ao.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ao@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ao@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ao.imageset/ao@2x.png b/Passepartout/App/Flags.xcassets/flags/ao.imageset/ao@2x.png deleted file mode 100644 index 24a1c52a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ao.imageset/ao@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ao.imageset/ao@3x.png b/Passepartout/App/Flags.xcassets/flags/ao.imageset/ao@3x.png deleted file mode 100644 index e5e09c97..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ao.imageset/ao@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/aq.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/aq.imageset/Contents.json deleted file mode 100644 index f6979ace..00000000 --- a/Passepartout/App/Flags.xcassets/flags/aq.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "aq@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "aq@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/aq.imageset/aq@2x.png b/Passepartout/App/Flags.xcassets/flags/aq.imageset/aq@2x.png deleted file mode 100644 index 96f2dd18..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/aq.imageset/aq@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/aq.imageset/aq@3x.png b/Passepartout/App/Flags.xcassets/flags/aq.imageset/aq@3x.png deleted file mode 100644 index 373fb3d3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/aq.imageset/aq@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ar.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ar.imageset/Contents.json deleted file mode 100644 index 540e2b7b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ar.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ar@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ar@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ar.imageset/ar@2x.png b/Passepartout/App/Flags.xcassets/flags/ar.imageset/ar@2x.png deleted file mode 100644 index 6bd4a830..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ar.imageset/ar@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ar.imageset/ar@3x.png b/Passepartout/App/Flags.xcassets/flags/ar.imageset/ar@3x.png deleted file mode 100644 index 63b24e13..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ar.imageset/ar@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/as.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/as.imageset/Contents.json deleted file mode 100644 index ce2c26cc..00000000 --- a/Passepartout/App/Flags.xcassets/flags/as.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "as@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "as@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/as.imageset/as@2x.png b/Passepartout/App/Flags.xcassets/flags/as.imageset/as@2x.png deleted file mode 100644 index a5311f70..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/as.imageset/as@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/as.imageset/as@3x.png b/Passepartout/App/Flags.xcassets/flags/as.imageset/as@3x.png deleted file mode 100644 index d35b56d2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/as.imageset/as@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/at.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/at.imageset/Contents.json deleted file mode 100644 index 05bdb0a5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/at.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "at@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "at@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/at.imageset/at@2x.png b/Passepartout/App/Flags.xcassets/flags/at.imageset/at@2x.png deleted file mode 100644 index c5f0fdaa..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/at.imageset/at@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/at.imageset/at@3x.png b/Passepartout/App/Flags.xcassets/flags/at.imageset/at@3x.png deleted file mode 100644 index a5fbad83..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/at.imageset/at@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/au.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/au.imageset/Contents.json deleted file mode 100644 index 069342c5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/au.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "au@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "au@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/au.imageset/au@2x.png b/Passepartout/App/Flags.xcassets/flags/au.imageset/au@2x.png deleted file mode 100644 index fb2b9e66..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/au.imageset/au@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/au.imageset/au@3x.png b/Passepartout/App/Flags.xcassets/flags/au.imageset/au@3x.png deleted file mode 100644 index 78959d50..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/au.imageset/au@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/aw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/aw.imageset/Contents.json deleted file mode 100644 index 08ec8e6b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/aw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "aw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "aw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/aw.imageset/aw@2x.png b/Passepartout/App/Flags.xcassets/flags/aw.imageset/aw@2x.png deleted file mode 100644 index 5d7d3474..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/aw.imageset/aw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/aw.imageset/aw@3x.png b/Passepartout/App/Flags.xcassets/flags/aw.imageset/aw@3x.png deleted file mode 100644 index 0bc8158c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/aw.imageset/aw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ax.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ax.imageset/Contents.json deleted file mode 100644 index b5ba2c77..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ax.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ax@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ax@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ax.imageset/ax@2x.png b/Passepartout/App/Flags.xcassets/flags/ax.imageset/ax@2x.png deleted file mode 100644 index 044c6282..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ax.imageset/ax@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ax.imageset/ax@3x.png b/Passepartout/App/Flags.xcassets/flags/ax.imageset/ax@3x.png deleted file mode 100644 index 71bfa28a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ax.imageset/ax@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/az.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/az.imageset/Contents.json deleted file mode 100644 index 467ecc6d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/az.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "az@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "az@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/az.imageset/az@2x.png b/Passepartout/App/Flags.xcassets/flags/az.imageset/az@2x.png deleted file mode 100644 index 0b0bcdff..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/az.imageset/az@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/az.imageset/az@3x.png b/Passepartout/App/Flags.xcassets/flags/az.imageset/az@3x.png deleted file mode 100644 index fffef425..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/az.imageset/az@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ba.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ba.imageset/Contents.json deleted file mode 100644 index 20c2a32b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ba.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ba@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ba@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ba.imageset/ba@2x.png b/Passepartout/App/Flags.xcassets/flags/ba.imageset/ba@2x.png deleted file mode 100644 index 10f50c24..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ba.imageset/ba@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ba.imageset/ba@3x.png b/Passepartout/App/Flags.xcassets/flags/ba.imageset/ba@3x.png deleted file mode 100644 index e32c6c3d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ba.imageset/ba@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bb.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bb.imageset/Contents.json deleted file mode 100644 index 674dc77e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bb.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bb@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bb@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bb.imageset/bb@2x.png b/Passepartout/App/Flags.xcassets/flags/bb.imageset/bb@2x.png deleted file mode 100644 index 51292d04..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bb.imageset/bb@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bb.imageset/bb@3x.png b/Passepartout/App/Flags.xcassets/flags/bb.imageset/bb@3x.png deleted file mode 100644 index 778d2fda..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bb.imageset/bb@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bd.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bd.imageset/Contents.json deleted file mode 100644 index d756d84f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bd.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bd@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bd@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bd.imageset/bd@2x.png b/Passepartout/App/Flags.xcassets/flags/bd.imageset/bd@2x.png deleted file mode 100644 index 47aa9ad6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bd.imageset/bd@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bd.imageset/bd@3x.png b/Passepartout/App/Flags.xcassets/flags/bd.imageset/bd@3x.png deleted file mode 100644 index 1fa81d82..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bd.imageset/bd@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/be.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/be.imageset/Contents.json deleted file mode 100644 index 1f7f4e05..00000000 --- a/Passepartout/App/Flags.xcassets/flags/be.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "be@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "be@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/be.imageset/be@2x.png b/Passepartout/App/Flags.xcassets/flags/be.imageset/be@2x.png deleted file mode 100644 index 4d04da30..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/be.imageset/be@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/be.imageset/be@3x.png b/Passepartout/App/Flags.xcassets/flags/be.imageset/be@3x.png deleted file mode 100644 index bca195cc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/be.imageset/be@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bf.imageset/Contents.json deleted file mode 100644 index 9faa797e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bf.imageset/bf@2x.png b/Passepartout/App/Flags.xcassets/flags/bf.imageset/bf@2x.png deleted file mode 100644 index bbf7fedf..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bf.imageset/bf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bf.imageset/bf@3x.png b/Passepartout/App/Flags.xcassets/flags/bf.imageset/bf@3x.png deleted file mode 100644 index a20c55f4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bf.imageset/bf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bg.imageset/Contents.json deleted file mode 100644 index 6ed3e15d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bg.imageset/bg@2x.png b/Passepartout/App/Flags.xcassets/flags/bg.imageset/bg@2x.png deleted file mode 100644 index 081bcfb0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bg.imageset/bg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bg.imageset/bg@3x.png b/Passepartout/App/Flags.xcassets/flags/bg.imageset/bg@3x.png deleted file mode 100644 index fbea9386..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bg.imageset/bg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bh.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bh.imageset/Contents.json deleted file mode 100644 index 0e554092..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bh.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bh@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bh@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bh.imageset/bh@2x.png b/Passepartout/App/Flags.xcassets/flags/bh.imageset/bh@2x.png deleted file mode 100644 index 8f3f9221..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bh.imageset/bh@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bh.imageset/bh@3x.png b/Passepartout/App/Flags.xcassets/flags/bh.imageset/bh@3x.png deleted file mode 100644 index 71e26d3a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bh.imageset/bh@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bi.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bi.imageset/Contents.json deleted file mode 100644 index 5367f5f4..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bi.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bi@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bi@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bi.imageset/bi@2x.png b/Passepartout/App/Flags.xcassets/flags/bi.imageset/bi@2x.png deleted file mode 100644 index fca8e385..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bi.imageset/bi@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bi.imageset/bi@3x.png b/Passepartout/App/Flags.xcassets/flags/bi.imageset/bi@3x.png deleted file mode 100644 index 79500be1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bi.imageset/bi@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bj.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bj.imageset/Contents.json deleted file mode 100644 index d3d37453..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bj.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bj@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bj@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bj.imageset/bj@2x.png b/Passepartout/App/Flags.xcassets/flags/bj.imageset/bj@2x.png deleted file mode 100644 index 3bfebc51..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bj.imageset/bj@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bj.imageset/bj@3x.png b/Passepartout/App/Flags.xcassets/flags/bj.imageset/bj@3x.png deleted file mode 100644 index 49d31025..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bj.imageset/bj@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bl.imageset/Contents.json deleted file mode 100644 index 59cca760..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bl.imageset/bl@2x.png b/Passepartout/App/Flags.xcassets/flags/bl.imageset/bl@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bl.imageset/bl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bl.imageset/bl@3x.png b/Passepartout/App/Flags.xcassets/flags/bl.imageset/bl@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bl.imageset/bl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bm.imageset/Contents.json deleted file mode 100644 index 2435e7ed..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bm.imageset/bm@2x.png b/Passepartout/App/Flags.xcassets/flags/bm.imageset/bm@2x.png deleted file mode 100644 index d0f302a5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bm.imageset/bm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bm.imageset/bm@3x.png b/Passepartout/App/Flags.xcassets/flags/bm.imageset/bm@3x.png deleted file mode 100644 index 8e903d72..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bm.imageset/bm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bn.imageset/Contents.json deleted file mode 100644 index c6dbd79a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bn.imageset/bn@2x.png b/Passepartout/App/Flags.xcassets/flags/bn.imageset/bn@2x.png deleted file mode 100644 index b0af5e81..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bn.imageset/bn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bn.imageset/bn@3x.png b/Passepartout/App/Flags.xcassets/flags/bn.imageset/bn@3x.png deleted file mode 100644 index b97fde48..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bn.imageset/bn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bo.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bo.imageset/Contents.json deleted file mode 100644 index 2c4e6dfa..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bo.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bo@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bo@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bo.imageset/bo@2x.png b/Passepartout/App/Flags.xcassets/flags/bo.imageset/bo@2x.png deleted file mode 100644 index 330af1a1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bo.imageset/bo@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bo.imageset/bo@3x.png b/Passepartout/App/Flags.xcassets/flags/bo.imageset/bo@3x.png deleted file mode 100644 index 5d145c3d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bo.imageset/bo@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bq.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bq.imageset/Contents.json deleted file mode 100644 index 8c361eb7..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bq.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bq@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bq@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bq.imageset/bq@2x.png b/Passepartout/App/Flags.xcassets/flags/bq.imageset/bq@2x.png deleted file mode 100644 index 513d9ff7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bq.imageset/bq@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bq.imageset/bq@3x.png b/Passepartout/App/Flags.xcassets/flags/bq.imageset/bq@3x.png deleted file mode 100644 index a05b157c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bq.imageset/bq@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/br.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/br.imageset/Contents.json deleted file mode 100644 index 3eb3ddb8..00000000 --- a/Passepartout/App/Flags.xcassets/flags/br.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "br@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "br@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/br.imageset/br@2x.png b/Passepartout/App/Flags.xcassets/flags/br.imageset/br@2x.png deleted file mode 100644 index 059f8a81..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/br.imageset/br@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/br.imageset/br@3x.png b/Passepartout/App/Flags.xcassets/flags/br.imageset/br@3x.png deleted file mode 100644 index a1cc40af..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/br.imageset/br@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bs.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bs.imageset/Contents.json deleted file mode 100644 index c28f5fec..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bs.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bs@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bs@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bs.imageset/bs@2x.png b/Passepartout/App/Flags.xcassets/flags/bs.imageset/bs@2x.png deleted file mode 100644 index c2e2290e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bs.imageset/bs@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bs.imageset/bs@3x.png b/Passepartout/App/Flags.xcassets/flags/bs.imageset/bs@3x.png deleted file mode 100644 index 2a081076..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bs.imageset/bs@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bt.imageset/Contents.json deleted file mode 100644 index 59623c89..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bt.imageset/bt@2x.png b/Passepartout/App/Flags.xcassets/flags/bt.imageset/bt@2x.png deleted file mode 100644 index 7727ed18..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bt.imageset/bt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bt.imageset/bt@3x.png b/Passepartout/App/Flags.xcassets/flags/bt.imageset/bt@3x.png deleted file mode 100644 index 26447ef3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bt.imageset/bt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bv.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bv.imageset/Contents.json deleted file mode 100644 index 69d8e0bd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bv.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bv@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bv@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bv.imageset/bv@2x.png b/Passepartout/App/Flags.xcassets/flags/bv.imageset/bv@2x.png deleted file mode 100644 index 84645f64..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bv.imageset/bv@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bv.imageset/bv@3x.png b/Passepartout/App/Flags.xcassets/flags/bv.imageset/bv@3x.png deleted file mode 100644 index e66ac431..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bv.imageset/bv@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bw.imageset/Contents.json deleted file mode 100644 index aa3d06de..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bw.imageset/bw@2x.png b/Passepartout/App/Flags.xcassets/flags/bw.imageset/bw@2x.png deleted file mode 100644 index 936beccd..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bw.imageset/bw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bw.imageset/bw@3x.png b/Passepartout/App/Flags.xcassets/flags/bw.imageset/bw@3x.png deleted file mode 100644 index 777de7be..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bw.imageset/bw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/by.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/by.imageset/Contents.json deleted file mode 100644 index f7b13fc6..00000000 --- a/Passepartout/App/Flags.xcassets/flags/by.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "by@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "by@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/by.imageset/by@2x.png b/Passepartout/App/Flags.xcassets/flags/by.imageset/by@2x.png deleted file mode 100644 index 68db605c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/by.imageset/by@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/by.imageset/by@3x.png b/Passepartout/App/Flags.xcassets/flags/by.imageset/by@3x.png deleted file mode 100644 index 7bdbd850..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/by.imageset/by@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/bz.imageset/Contents.json deleted file mode 100644 index 35c628e4..00000000 --- a/Passepartout/App/Flags.xcassets/flags/bz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "bz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "bz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/bz.imageset/bz@2x.png b/Passepartout/App/Flags.xcassets/flags/bz.imageset/bz@2x.png deleted file mode 100644 index 52c415de..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bz.imageset/bz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/bz.imageset/bz@3x.png b/Passepartout/App/Flags.xcassets/flags/bz.imageset/bz@3x.png deleted file mode 100644 index b1b1ccf0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/bz.imageset/bz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ca.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ca.imageset/Contents.json deleted file mode 100644 index 9090fb87..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ca.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ca@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ca@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ca.imageset/ca@2x.png b/Passepartout/App/Flags.xcassets/flags/ca.imageset/ca@2x.png deleted file mode 100644 index c685ec3c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ca.imageset/ca@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ca.imageset/ca@3x.png b/Passepartout/App/Flags.xcassets/flags/ca.imageset/ca@3x.png deleted file mode 100644 index 3a097500..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ca.imageset/ca@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cc.imageset/Contents.json deleted file mode 100644 index 4f10c903..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cc.imageset/cc@2x.png b/Passepartout/App/Flags.xcassets/flags/cc.imageset/cc@2x.png deleted file mode 100644 index 382d1673..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cc.imageset/cc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cc.imageset/cc@3x.png b/Passepartout/App/Flags.xcassets/flags/cc.imageset/cc@3x.png deleted file mode 100644 index 1101939d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cc.imageset/cc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cd.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cd.imageset/Contents.json deleted file mode 100644 index 3f149671..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cd.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cd@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cd@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cd.imageset/cd@2x.png b/Passepartout/App/Flags.xcassets/flags/cd.imageset/cd@2x.png deleted file mode 100644 index ea8a7598..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cd.imageset/cd@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cd.imageset/cd@3x.png b/Passepartout/App/Flags.xcassets/flags/cd.imageset/cd@3x.png deleted file mode 100644 index 483d57f8..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cd.imageset/cd@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cf.imageset/Contents.json deleted file mode 100644 index b7a75bdc..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cf.imageset/cf@2x.png b/Passepartout/App/Flags.xcassets/flags/cf.imageset/cf@2x.png deleted file mode 100644 index 2c92a9c9..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cf.imageset/cf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cf.imageset/cf@3x.png b/Passepartout/App/Flags.xcassets/flags/cf.imageset/cf@3x.png deleted file mode 100644 index 7a336adf..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cf.imageset/cf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cg.imageset/Contents.json deleted file mode 100644 index 3ad6e3e3..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cg.imageset/cg@2x.png b/Passepartout/App/Flags.xcassets/flags/cg.imageset/cg@2x.png deleted file mode 100644 index 5026abb7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cg.imageset/cg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cg.imageset/cg@3x.png b/Passepartout/App/Flags.xcassets/flags/cg.imageset/cg@3x.png deleted file mode 100644 index 851d3fac..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cg.imageset/cg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ch.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ch.imageset/Contents.json deleted file mode 100644 index 1d5681ce..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ch.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ch@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ch@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ch.imageset/ch@2x.png b/Passepartout/App/Flags.xcassets/flags/ch.imageset/ch@2x.png deleted file mode 100644 index 85751ffb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ch.imageset/ch@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ch.imageset/ch@3x.png b/Passepartout/App/Flags.xcassets/flags/ch.imageset/ch@3x.png deleted file mode 100644 index 4871e1eb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ch.imageset/ch@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ci.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ci.imageset/Contents.json deleted file mode 100644 index 4ec1ef56..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ci.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ci@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ci@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ci.imageset/ci@2x.png b/Passepartout/App/Flags.xcassets/flags/ci.imageset/ci@2x.png deleted file mode 100644 index f0e2a50a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ci.imageset/ci@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ci.imageset/ci@3x.png b/Passepartout/App/Flags.xcassets/flags/ci.imageset/ci@3x.png deleted file mode 100644 index 53691b75..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ci.imageset/ci@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ck.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ck.imageset/Contents.json deleted file mode 100644 index 4e637127..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ck.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ck@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ck@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ck.imageset/ck@2x.png b/Passepartout/App/Flags.xcassets/flags/ck.imageset/ck@2x.png deleted file mode 100644 index 6c11caf1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ck.imageset/ck@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ck.imageset/ck@3x.png b/Passepartout/App/Flags.xcassets/flags/ck.imageset/ck@3x.png deleted file mode 100644 index ed62ca54..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ck.imageset/ck@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cl.imageset/Contents.json deleted file mode 100644 index f329c863..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cl.imageset/cl@2x.png b/Passepartout/App/Flags.xcassets/flags/cl.imageset/cl@2x.png deleted file mode 100644 index 253f1c0b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cl.imageset/cl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cl.imageset/cl@3x.png b/Passepartout/App/Flags.xcassets/flags/cl.imageset/cl@3x.png deleted file mode 100644 index 306084f4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cl.imageset/cl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cm.imageset/Contents.json deleted file mode 100644 index 40a18873..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cm.imageset/cm@2x.png b/Passepartout/App/Flags.xcassets/flags/cm.imageset/cm@2x.png deleted file mode 100644 index 7053990d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cm.imageset/cm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cm.imageset/cm@3x.png b/Passepartout/App/Flags.xcassets/flags/cm.imageset/cm@3x.png deleted file mode 100644 index d90a037a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cm.imageset/cm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cn.imageset/Contents.json deleted file mode 100644 index ca204426..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cn.imageset/cn@2x.png b/Passepartout/App/Flags.xcassets/flags/cn.imageset/cn@2x.png deleted file mode 100644 index b5a86922..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cn.imageset/cn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cn.imageset/cn@3x.png b/Passepartout/App/Flags.xcassets/flags/cn.imageset/cn@3x.png deleted file mode 100644 index 589cc3a0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cn.imageset/cn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/co.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/co.imageset/Contents.json deleted file mode 100644 index e6ead9c7..00000000 --- a/Passepartout/App/Flags.xcassets/flags/co.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "co@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "co@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/co.imageset/co@2x.png b/Passepartout/App/Flags.xcassets/flags/co.imageset/co@2x.png deleted file mode 100644 index f5f0d008..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/co.imageset/co@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/co.imageset/co@3x.png b/Passepartout/App/Flags.xcassets/flags/co.imageset/co@3x.png deleted file mode 100644 index 9149689d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/co.imageset/co@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cr.imageset/Contents.json deleted file mode 100644 index b86ea704..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cr.imageset/cr@2x.png b/Passepartout/App/Flags.xcassets/flags/cr.imageset/cr@2x.png deleted file mode 100644 index db4d8a57..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cr.imageset/cr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cr.imageset/cr@3x.png b/Passepartout/App/Flags.xcassets/flags/cr.imageset/cr@3x.png deleted file mode 100644 index 74ceade1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cr.imageset/cr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cu.imageset/Contents.json deleted file mode 100644 index cd022b9a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cu.imageset/cu@2x.png b/Passepartout/App/Flags.xcassets/flags/cu.imageset/cu@2x.png deleted file mode 100644 index f5ceac75..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cu.imageset/cu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cu.imageset/cu@3x.png b/Passepartout/App/Flags.xcassets/flags/cu.imageset/cu@3x.png deleted file mode 100644 index b5949986..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cu.imageset/cu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cv.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cv.imageset/Contents.json deleted file mode 100644 index b0c64157..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cv.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cv@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cv@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cv.imageset/cv@2x.png b/Passepartout/App/Flags.xcassets/flags/cv.imageset/cv@2x.png deleted file mode 100644 index 9c1ef2a4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cv.imageset/cv@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cv.imageset/cv@3x.png b/Passepartout/App/Flags.xcassets/flags/cv.imageset/cv@3x.png deleted file mode 100644 index df529c75..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cv.imageset/cv@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cw.imageset/Contents.json deleted file mode 100644 index 4f8897e9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cw.imageset/cw@2x.png b/Passepartout/App/Flags.xcassets/flags/cw.imageset/cw@2x.png deleted file mode 100644 index df79c0ec..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cw.imageset/cw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cw.imageset/cw@3x.png b/Passepartout/App/Flags.xcassets/flags/cw.imageset/cw@3x.png deleted file mode 100644 index 534b1540..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cw.imageset/cw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cx.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cx.imageset/Contents.json deleted file mode 100644 index 6074dd4c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cx.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cx@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cx@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cx.imageset/cx@2x.png b/Passepartout/App/Flags.xcassets/flags/cx.imageset/cx@2x.png deleted file mode 100644 index d6186a0f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cx.imageset/cx@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cx.imageset/cx@3x.png b/Passepartout/App/Flags.xcassets/flags/cx.imageset/cx@3x.png deleted file mode 100644 index 2f2817d2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cx.imageset/cx@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cy.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cy.imageset/Contents.json deleted file mode 100644 index 7ce08eca..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cy.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cy@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cy@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cy.imageset/cy@2x.png b/Passepartout/App/Flags.xcassets/flags/cy.imageset/cy@2x.png deleted file mode 100644 index 58901ed4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cy.imageset/cy@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cy.imageset/cy@3x.png b/Passepartout/App/Flags.xcassets/flags/cy.imageset/cy@3x.png deleted file mode 100644 index 69c14025..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cy.imageset/cy@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/cz.imageset/Contents.json deleted file mode 100644 index c7326443..00000000 --- a/Passepartout/App/Flags.xcassets/flags/cz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "cz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "cz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/cz.imageset/cz@2x.png b/Passepartout/App/Flags.xcassets/flags/cz.imageset/cz@2x.png deleted file mode 100644 index 90fa6940..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cz.imageset/cz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/cz.imageset/cz@3x.png b/Passepartout/App/Flags.xcassets/flags/cz.imageset/cz@3x.png deleted file mode 100644 index 51750eef..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/cz.imageset/cz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/de.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/de.imageset/Contents.json deleted file mode 100644 index a6648ba1..00000000 --- a/Passepartout/App/Flags.xcassets/flags/de.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "de@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "de@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/de.imageset/de@2x.png b/Passepartout/App/Flags.xcassets/flags/de.imageset/de@2x.png deleted file mode 100644 index c03c578f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/de.imageset/de@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/de.imageset/de@3x.png b/Passepartout/App/Flags.xcassets/flags/de.imageset/de@3x.png deleted file mode 100644 index ceec04b7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/de.imageset/de@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dj.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/dj.imageset/Contents.json deleted file mode 100644 index 64809945..00000000 --- a/Passepartout/App/Flags.xcassets/flags/dj.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "dj@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "dj@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/dj.imageset/dj@2x.png b/Passepartout/App/Flags.xcassets/flags/dj.imageset/dj@2x.png deleted file mode 100644 index 47234829..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dj.imageset/dj@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dj.imageset/dj@3x.png b/Passepartout/App/Flags.xcassets/flags/dj.imageset/dj@3x.png deleted file mode 100644 index fc569411..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dj.imageset/dj@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/dk.imageset/Contents.json deleted file mode 100644 index 1465b615..00000000 --- a/Passepartout/App/Flags.xcassets/flags/dk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "dk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "dk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/dk.imageset/dk@2x.png b/Passepartout/App/Flags.xcassets/flags/dk.imageset/dk@2x.png deleted file mode 100644 index bdfb114a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dk.imageset/dk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dk.imageset/dk@3x.png b/Passepartout/App/Flags.xcassets/flags/dk.imageset/dk@3x.png deleted file mode 100644 index bc70f892..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dk.imageset/dk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/dm.imageset/Contents.json deleted file mode 100644 index c96c4669..00000000 --- a/Passepartout/App/Flags.xcassets/flags/dm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "dm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "dm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/dm.imageset/dm@2x.png b/Passepartout/App/Flags.xcassets/flags/dm.imageset/dm@2x.png deleted file mode 100644 index dc11d351..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dm.imageset/dm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dm.imageset/dm@3x.png b/Passepartout/App/Flags.xcassets/flags/dm.imageset/dm@3x.png deleted file mode 100644 index bc6fc08e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dm.imageset/dm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/do.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/do.imageset/Contents.json deleted file mode 100644 index bd6c28e6..00000000 --- a/Passepartout/App/Flags.xcassets/flags/do.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "do@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "do@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/do.imageset/do@2x.png b/Passepartout/App/Flags.xcassets/flags/do.imageset/do@2x.png deleted file mode 100644 index 1f9c2dcf..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/do.imageset/do@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/do.imageset/do@3x.png b/Passepartout/App/Flags.xcassets/flags/do.imageset/do@3x.png deleted file mode 100644 index 1e1a1cbf..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/do.imageset/do@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/dz.imageset/Contents.json deleted file mode 100644 index 431e938c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/dz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "dz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "dz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/dz.imageset/dz@2x.png b/Passepartout/App/Flags.xcassets/flags/dz.imageset/dz@2x.png deleted file mode 100644 index ebd8e0dd..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dz.imageset/dz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/dz.imageset/dz@3x.png b/Passepartout/App/Flags.xcassets/flags/dz.imageset/dz@3x.png deleted file mode 100644 index 15a817d6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/dz.imageset/dz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ec.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ec.imageset/Contents.json deleted file mode 100644 index 33c89f9f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ec.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ec@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ec@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ec.imageset/ec@2x.png b/Passepartout/App/Flags.xcassets/flags/ec.imageset/ec@2x.png deleted file mode 100644 index 6960afa5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ec.imageset/ec@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ec.imageset/ec@3x.png b/Passepartout/App/Flags.xcassets/flags/ec.imageset/ec@3x.png deleted file mode 100644 index 4b090f96..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ec.imageset/ec@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ee.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ee.imageset/Contents.json deleted file mode 100644 index e96a6b27..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ee.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ee@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ee@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ee.imageset/ee@2x.png b/Passepartout/App/Flags.xcassets/flags/ee.imageset/ee@2x.png deleted file mode 100644 index 51d1c7cc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ee.imageset/ee@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ee.imageset/ee@3x.png b/Passepartout/App/Flags.xcassets/flags/ee.imageset/ee@3x.png deleted file mode 100644 index c5309249..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ee.imageset/ee@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/eg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/eg.imageset/Contents.json deleted file mode 100644 index a0639c9c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/eg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "eg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "eg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/eg.imageset/eg@2x.png b/Passepartout/App/Flags.xcassets/flags/eg.imageset/eg@2x.png deleted file mode 100644 index 1e42c0fc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/eg.imageset/eg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/eg.imageset/eg@3x.png b/Passepartout/App/Flags.xcassets/flags/eg.imageset/eg@3x.png deleted file mode 100644 index a88188bb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/eg.imageset/eg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/eh.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/eh.imageset/Contents.json deleted file mode 100644 index 6f4e2f95..00000000 --- a/Passepartout/App/Flags.xcassets/flags/eh.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "eh@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "eh@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/eh.imageset/eh@2x.png b/Passepartout/App/Flags.xcassets/flags/eh.imageset/eh@2x.png deleted file mode 100644 index 5f84b499..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/eh.imageset/eh@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/eh.imageset/eh@3x.png b/Passepartout/App/Flags.xcassets/flags/eh.imageset/eh@3x.png deleted file mode 100644 index 0b23bd69..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/eh.imageset/eh@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/er.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/er.imageset/Contents.json deleted file mode 100644 index 555b3cbd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/er.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "er@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "er@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/er.imageset/er@2x.png b/Passepartout/App/Flags.xcassets/flags/er.imageset/er@2x.png deleted file mode 100644 index 2c037a27..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/er.imageset/er@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/er.imageset/er@3x.png b/Passepartout/App/Flags.xcassets/flags/er.imageset/er@3x.png deleted file mode 100644 index afc1b43c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/er.imageset/er@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/Contents.json deleted file mode 100644 index 8db5b0ae..00000000 --- a/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "es-ct@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "es-ct@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/es-ct@2x.png b/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/es-ct@2x.png deleted file mode 100644 index ce6e9db4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/es-ct@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/es-ct@3x.png b/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/es-ct@3x.png deleted file mode 100644 index aac4a99b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/es-ct.imageset/es-ct@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/es.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/es.imageset/Contents.json deleted file mode 100644 index b70dcd3f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/es.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "es@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "es@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/es.imageset/es@2x.png b/Passepartout/App/Flags.xcassets/flags/es.imageset/es@2x.png deleted file mode 100644 index 1bc4d4ce..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/es.imageset/es@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/es.imageset/es@3x.png b/Passepartout/App/Flags.xcassets/flags/es.imageset/es@3x.png deleted file mode 100644 index 2cde9c1f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/es.imageset/es@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/et.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/et.imageset/Contents.json deleted file mode 100644 index 45f68159..00000000 --- a/Passepartout/App/Flags.xcassets/flags/et.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "et@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "et@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/et.imageset/et@2x.png b/Passepartout/App/Flags.xcassets/flags/et.imageset/et@2x.png deleted file mode 100644 index 9db13a87..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/et.imageset/et@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/et.imageset/et@3x.png b/Passepartout/App/Flags.xcassets/flags/et.imageset/et@3x.png deleted file mode 100644 index 21bfc0c4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/et.imageset/et@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/eu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/eu.imageset/Contents.json deleted file mode 100644 index 70ec8ab9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/eu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "eu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "eu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/eu.imageset/eu@2x.png b/Passepartout/App/Flags.xcassets/flags/eu.imageset/eu@2x.png deleted file mode 100644 index 2ae9b906..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/eu.imageset/eu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/eu.imageset/eu@3x.png b/Passepartout/App/Flags.xcassets/flags/eu.imageset/eu@3x.png deleted file mode 100644 index 14ed86e6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/eu.imageset/eu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fi.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/fi.imageset/Contents.json deleted file mode 100644 index a84ff1ca..00000000 --- a/Passepartout/App/Flags.xcassets/flags/fi.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "fi@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "fi@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/fi.imageset/fi@2x.png b/Passepartout/App/Flags.xcassets/flags/fi.imageset/fi@2x.png deleted file mode 100644 index 979b7e95..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fi.imageset/fi@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fi.imageset/fi@3x.png b/Passepartout/App/Flags.xcassets/flags/fi.imageset/fi@3x.png deleted file mode 100644 index dc68fe94..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fi.imageset/fi@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fj.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/fj.imageset/Contents.json deleted file mode 100644 index d36f15ab..00000000 --- a/Passepartout/App/Flags.xcassets/flags/fj.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "fj@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "fj@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/fj.imageset/fj@2x.png b/Passepartout/App/Flags.xcassets/flags/fj.imageset/fj@2x.png deleted file mode 100644 index 41dc2a93..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fj.imageset/fj@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fj.imageset/fj@3x.png b/Passepartout/App/Flags.xcassets/flags/fj.imageset/fj@3x.png deleted file mode 100644 index 9d04df4e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fj.imageset/fj@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/fk.imageset/Contents.json deleted file mode 100644 index 5c0391b1..00000000 --- a/Passepartout/App/Flags.xcassets/flags/fk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "fk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "fk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/fk.imageset/fk@2x.png b/Passepartout/App/Flags.xcassets/flags/fk.imageset/fk@2x.png deleted file mode 100644 index f0d21fca..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fk.imageset/fk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fk.imageset/fk@3x.png b/Passepartout/App/Flags.xcassets/flags/fk.imageset/fk@3x.png deleted file mode 100644 index bce2633b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fk.imageset/fk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/fm.imageset/Contents.json deleted file mode 100644 index 4f6defce..00000000 --- a/Passepartout/App/Flags.xcassets/flags/fm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "fm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "fm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/fm.imageset/fm@2x.png b/Passepartout/App/Flags.xcassets/flags/fm.imageset/fm@2x.png deleted file mode 100644 index 28c0ddbc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fm.imageset/fm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fm.imageset/fm@3x.png b/Passepartout/App/Flags.xcassets/flags/fm.imageset/fm@3x.png deleted file mode 100644 index 6701b51f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fm.imageset/fm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fo.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/fo.imageset/Contents.json deleted file mode 100644 index fd4e7136..00000000 --- a/Passepartout/App/Flags.xcassets/flags/fo.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "fo@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "fo@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/fo.imageset/fo@2x.png b/Passepartout/App/Flags.xcassets/flags/fo.imageset/fo@2x.png deleted file mode 100644 index 7f8cfc22..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fo.imageset/fo@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fo.imageset/fo@3x.png b/Passepartout/App/Flags.xcassets/flags/fo.imageset/fo@3x.png deleted file mode 100644 index 4e7ea315..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fo.imageset/fo@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/fr.imageset/Contents.json deleted file mode 100644 index e8a2d126..00000000 --- a/Passepartout/App/Flags.xcassets/flags/fr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "fr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "fr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/fr.imageset/fr@2x.png b/Passepartout/App/Flags.xcassets/flags/fr.imageset/fr@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fr.imageset/fr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/fr.imageset/fr@3x.png b/Passepartout/App/Flags.xcassets/flags/fr.imageset/fr@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/fr.imageset/fr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ga.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ga.imageset/Contents.json deleted file mode 100644 index f8b037ff..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ga.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ga@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ga@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ga.imageset/ga@2x.png b/Passepartout/App/Flags.xcassets/flags/ga.imageset/ga@2x.png deleted file mode 100644 index f368497e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ga.imageset/ga@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ga.imageset/ga@3x.png b/Passepartout/App/Flags.xcassets/flags/ga.imageset/ga@3x.png deleted file mode 100644 index 00db26cc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ga.imageset/ga@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/Contents.json deleted file mode 100644 index ebac6de7..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gb-eng@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gb-eng@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/gb-eng@2x.png b/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/gb-eng@2x.png deleted file mode 100644 index 1d70eb09..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/gb-eng@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/gb-eng@3x.png b/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/gb-eng@3x.png deleted file mode 100644 index dca2f90d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-eng.imageset/gb-eng@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/Contents.json deleted file mode 100644 index 06765425..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gb-nir@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gb-nir@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/gb-nir@2x.png b/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/gb-nir@2x.png deleted file mode 100644 index ed6a5735..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/gb-nir@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/gb-nir@3x.png b/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/gb-nir@3x.png deleted file mode 100644 index b12f0a03..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-nir.imageset/gb-nir@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/Contents.json deleted file mode 100644 index a2d9bac3..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gb-sct@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gb-sct@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/gb-sct@2x.png b/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/gb-sct@2x.png deleted file mode 100644 index 523907d4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/gb-sct@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/gb-sct@3x.png b/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/gb-sct@3x.png deleted file mode 100644 index 7b76e967..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-sct.imageset/gb-sct@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/Contents.json deleted file mode 100644 index 432f48ff..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gb-wls@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gb-wls@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/gb-wls@2x.png b/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/gb-wls@2x.png deleted file mode 100644 index 166c33f8..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/gb-wls@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/gb-wls@3x.png b/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/gb-wls@3x.png deleted file mode 100644 index 89b31deb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb-wls.imageset/gb-wls@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gb.imageset/Contents.json deleted file mode 100644 index c4e27395..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gb.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gb@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gb@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gb.imageset/gb@2x.png b/Passepartout/App/Flags.xcassets/flags/gb.imageset/gb@2x.png deleted file mode 100644 index b6bfc9e7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb.imageset/gb@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gb.imageset/gb@3x.png b/Passepartout/App/Flags.xcassets/flags/gb.imageset/gb@3x.png deleted file mode 100644 index 3f5e3b61..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gb.imageset/gb@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gd.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gd.imageset/Contents.json deleted file mode 100644 index 3fd68edf..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gd.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gd@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gd@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gd.imageset/gd@2x.png b/Passepartout/App/Flags.xcassets/flags/gd.imageset/gd@2x.png deleted file mode 100644 index 80a0179d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gd.imageset/gd@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gd.imageset/gd@3x.png b/Passepartout/App/Flags.xcassets/flags/gd.imageset/gd@3x.png deleted file mode 100644 index 4139432c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gd.imageset/gd@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ge.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ge.imageset/Contents.json deleted file mode 100644 index ca3dbb2f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ge.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ge@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ge@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ge.imageset/ge@2x.png b/Passepartout/App/Flags.xcassets/flags/ge.imageset/ge@2x.png deleted file mode 100644 index e8d378fc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ge.imageset/ge@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ge.imageset/ge@3x.png b/Passepartout/App/Flags.xcassets/flags/ge.imageset/ge@3x.png deleted file mode 100644 index b1653ee1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ge.imageset/ge@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gf.imageset/Contents.json deleted file mode 100644 index 70c3eb67..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gf.imageset/gf@2x.png b/Passepartout/App/Flags.xcassets/flags/gf.imageset/gf@2x.png deleted file mode 100644 index d8eb033c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gf.imageset/gf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gf.imageset/gf@3x.png b/Passepartout/App/Flags.xcassets/flags/gf.imageset/gf@3x.png deleted file mode 100644 index ebcfdaad..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gf.imageset/gf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gg.imageset/Contents.json deleted file mode 100644 index fe3ea1a1..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gg.imageset/gg@2x.png b/Passepartout/App/Flags.xcassets/flags/gg.imageset/gg@2x.png deleted file mode 100644 index 7e5cb048..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gg.imageset/gg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gg.imageset/gg@3x.png b/Passepartout/App/Flags.xcassets/flags/gg.imageset/gg@3x.png deleted file mode 100644 index d0b7f0e7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gg.imageset/gg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gh.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gh.imageset/Contents.json deleted file mode 100644 index d15231e6..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gh.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gh@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gh@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gh.imageset/gh@2x.png b/Passepartout/App/Flags.xcassets/flags/gh.imageset/gh@2x.png deleted file mode 100644 index 91c844e7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gh.imageset/gh@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gh.imageset/gh@3x.png b/Passepartout/App/Flags.xcassets/flags/gh.imageset/gh@3x.png deleted file mode 100644 index 731e0dd7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gh.imageset/gh@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gi.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gi.imageset/Contents.json deleted file mode 100644 index 75faa11d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gi.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gi@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gi@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gi.imageset/gi@2x.png b/Passepartout/App/Flags.xcassets/flags/gi.imageset/gi@2x.png deleted file mode 100644 index ceeaa1ea..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gi.imageset/gi@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gi.imageset/gi@3x.png b/Passepartout/App/Flags.xcassets/flags/gi.imageset/gi@3x.png deleted file mode 100644 index b3db5eee..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gi.imageset/gi@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gl.imageset/Contents.json deleted file mode 100644 index 399dacbc..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gl.imageset/gl@2x.png b/Passepartout/App/Flags.xcassets/flags/gl.imageset/gl@2x.png deleted file mode 100644 index f58ce1c0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gl.imageset/gl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gl.imageset/gl@3x.png b/Passepartout/App/Flags.xcassets/flags/gl.imageset/gl@3x.png deleted file mode 100644 index d61a0aea..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gl.imageset/gl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gm.imageset/Contents.json deleted file mode 100644 index eb99bd62..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gm.imageset/gm@2x.png b/Passepartout/App/Flags.xcassets/flags/gm.imageset/gm@2x.png deleted file mode 100644 index 9bcf4a00..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gm.imageset/gm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gm.imageset/gm@3x.png b/Passepartout/App/Flags.xcassets/flags/gm.imageset/gm@3x.png deleted file mode 100644 index 54437547..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gm.imageset/gm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gn.imageset/Contents.json deleted file mode 100644 index 09710fc5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gn.imageset/gn@2x.png b/Passepartout/App/Flags.xcassets/flags/gn.imageset/gn@2x.png deleted file mode 100644 index 9833fd97..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gn.imageset/gn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gn.imageset/gn@3x.png b/Passepartout/App/Flags.xcassets/flags/gn.imageset/gn@3x.png deleted file mode 100644 index 8b75dd4c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gn.imageset/gn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gp.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gp.imageset/Contents.json deleted file mode 100644 index e31b6f54..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gp.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gp@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gp@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gp.imageset/gp@2x.png b/Passepartout/App/Flags.xcassets/flags/gp.imageset/gp@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gp.imageset/gp@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gp.imageset/gp@3x.png b/Passepartout/App/Flags.xcassets/flags/gp.imageset/gp@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gp.imageset/gp@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gq.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gq.imageset/Contents.json deleted file mode 100644 index 311477f7..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gq.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gq@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gq@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gq.imageset/gq@2x.png b/Passepartout/App/Flags.xcassets/flags/gq.imageset/gq@2x.png deleted file mode 100644 index fc645518..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gq.imageset/gq@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gq.imageset/gq@3x.png b/Passepartout/App/Flags.xcassets/flags/gq.imageset/gq@3x.png deleted file mode 100644 index 991329d6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gq.imageset/gq@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gr.imageset/Contents.json deleted file mode 100644 index 346cdd61..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gr.imageset/gr@2x.png b/Passepartout/App/Flags.xcassets/flags/gr.imageset/gr@2x.png deleted file mode 100644 index 35ab5a4c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gr.imageset/gr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gr.imageset/gr@3x.png b/Passepartout/App/Flags.xcassets/flags/gr.imageset/gr@3x.png deleted file mode 100644 index bd9e6c61..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gr.imageset/gr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gs.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gs.imageset/Contents.json deleted file mode 100644 index 5845c325..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gs.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gs@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gs@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gs.imageset/gs@2x.png b/Passepartout/App/Flags.xcassets/flags/gs.imageset/gs@2x.png deleted file mode 100644 index e1ea84ea..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gs.imageset/gs@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gs.imageset/gs@3x.png b/Passepartout/App/Flags.xcassets/flags/gs.imageset/gs@3x.png deleted file mode 100644 index 44b44859..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gs.imageset/gs@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gt.imageset/Contents.json deleted file mode 100644 index 3984f3a1..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gt.imageset/gt@2x.png b/Passepartout/App/Flags.xcassets/flags/gt.imageset/gt@2x.png deleted file mode 100644 index 10f98a35..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gt.imageset/gt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gt.imageset/gt@3x.png b/Passepartout/App/Flags.xcassets/flags/gt.imageset/gt@3x.png deleted file mode 100644 index 9ca142ba..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gt.imageset/gt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gu.imageset/Contents.json deleted file mode 100644 index 0f5f389a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gu.imageset/gu@2x.png b/Passepartout/App/Flags.xcassets/flags/gu.imageset/gu@2x.png deleted file mode 100644 index 524822d5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gu.imageset/gu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gu.imageset/gu@3x.png b/Passepartout/App/Flags.xcassets/flags/gu.imageset/gu@3x.png deleted file mode 100644 index 4210206e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gu.imageset/gu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gw.imageset/Contents.json deleted file mode 100644 index 6fa4eaaa..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gw.imageset/gw@2x.png b/Passepartout/App/Flags.xcassets/flags/gw.imageset/gw@2x.png deleted file mode 100644 index 7ed07727..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gw.imageset/gw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gw.imageset/gw@3x.png b/Passepartout/App/Flags.xcassets/flags/gw.imageset/gw@3x.png deleted file mode 100644 index 1c4310d5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gw.imageset/gw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gy.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/gy.imageset/Contents.json deleted file mode 100644 index 3cd4b397..00000000 --- a/Passepartout/App/Flags.xcassets/flags/gy.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "gy@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "gy@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/gy.imageset/gy@2x.png b/Passepartout/App/Flags.xcassets/flags/gy.imageset/gy@2x.png deleted file mode 100644 index 68fe62ec..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gy.imageset/gy@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/gy.imageset/gy@3x.png b/Passepartout/App/Flags.xcassets/flags/gy.imageset/gy@3x.png deleted file mode 100644 index 1d6e6368..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/gy.imageset/gy@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/hk.imageset/Contents.json deleted file mode 100644 index 60b14d75..00000000 --- a/Passepartout/App/Flags.xcassets/flags/hk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "hk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "hk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/hk.imageset/hk@2x.png b/Passepartout/App/Flags.xcassets/flags/hk.imageset/hk@2x.png deleted file mode 100644 index d180205f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hk.imageset/hk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hk.imageset/hk@3x.png b/Passepartout/App/Flags.xcassets/flags/hk.imageset/hk@3x.png deleted file mode 100644 index 88e52eef..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hk.imageset/hk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/hm.imageset/Contents.json deleted file mode 100644 index cca51f0b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/hm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "hm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "hm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/hm.imageset/hm@2x.png b/Passepartout/App/Flags.xcassets/flags/hm.imageset/hm@2x.png deleted file mode 100644 index 4b276032..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hm.imageset/hm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hm.imageset/hm@3x.png b/Passepartout/App/Flags.xcassets/flags/hm.imageset/hm@3x.png deleted file mode 100644 index 0193b691..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hm.imageset/hm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/hn.imageset/Contents.json deleted file mode 100644 index c4c5c676..00000000 --- a/Passepartout/App/Flags.xcassets/flags/hn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "hn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "hn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/hn.imageset/hn@2x.png b/Passepartout/App/Flags.xcassets/flags/hn.imageset/hn@2x.png deleted file mode 100644 index f6a9c31a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hn.imageset/hn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hn.imageset/hn@3x.png b/Passepartout/App/Flags.xcassets/flags/hn.imageset/hn@3x.png deleted file mode 100644 index 427ccf7c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hn.imageset/hn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/hr.imageset/Contents.json deleted file mode 100644 index b170577c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/hr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "hr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "hr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/hr.imageset/hr@2x.png b/Passepartout/App/Flags.xcassets/flags/hr.imageset/hr@2x.png deleted file mode 100644 index 12bbe304..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hr.imageset/hr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hr.imageset/hr@3x.png b/Passepartout/App/Flags.xcassets/flags/hr.imageset/hr@3x.png deleted file mode 100644 index c0de18f8..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hr.imageset/hr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ht.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ht.imageset/Contents.json deleted file mode 100644 index 02362ebd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ht.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ht@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ht@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ht.imageset/ht@2x.png b/Passepartout/App/Flags.xcassets/flags/ht.imageset/ht@2x.png deleted file mode 100644 index cea8c02d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ht.imageset/ht@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ht.imageset/ht@3x.png b/Passepartout/App/Flags.xcassets/flags/ht.imageset/ht@3x.png deleted file mode 100644 index 59396d3b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ht.imageset/ht@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/hu.imageset/Contents.json deleted file mode 100644 index 04c74089..00000000 --- a/Passepartout/App/Flags.xcassets/flags/hu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "hu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "hu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/hu.imageset/hu@2x.png b/Passepartout/App/Flags.xcassets/flags/hu.imageset/hu@2x.png deleted file mode 100644 index be900153..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hu.imageset/hu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/hu.imageset/hu@3x.png b/Passepartout/App/Flags.xcassets/flags/hu.imageset/hu@3x.png deleted file mode 100644 index eebefc04..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/hu.imageset/hu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/id.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/id.imageset/Contents.json deleted file mode 100644 index 72af7398..00000000 --- a/Passepartout/App/Flags.xcassets/flags/id.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "id@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "id@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/id.imageset/id@2x.png b/Passepartout/App/Flags.xcassets/flags/id.imageset/id@2x.png deleted file mode 100644 index 255d1a01..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/id.imageset/id@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/id.imageset/id@3x.png b/Passepartout/App/Flags.xcassets/flags/id.imageset/id@3x.png deleted file mode 100644 index 0587c8c0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/id.imageset/id@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ie.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ie.imageset/Contents.json deleted file mode 100644 index fe85e16c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ie.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ie@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ie@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ie.imageset/ie@2x.png b/Passepartout/App/Flags.xcassets/flags/ie.imageset/ie@2x.png deleted file mode 100644 index bc52ccdd..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ie.imageset/ie@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ie.imageset/ie@3x.png b/Passepartout/App/Flags.xcassets/flags/ie.imageset/ie@3x.png deleted file mode 100644 index 2a30d151..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ie.imageset/ie@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/il.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/il.imageset/Contents.json deleted file mode 100644 index 7faf42a7..00000000 --- a/Passepartout/App/Flags.xcassets/flags/il.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "il@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "il@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/il.imageset/il@2x.png b/Passepartout/App/Flags.xcassets/flags/il.imageset/il@2x.png deleted file mode 100644 index 7ab9bb15..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/il.imageset/il@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/il.imageset/il@3x.png b/Passepartout/App/Flags.xcassets/flags/il.imageset/il@3x.png deleted file mode 100644 index 1aad1e35..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/il.imageset/il@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/im.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/im.imageset/Contents.json deleted file mode 100644 index 54098bd5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/im.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "im@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "im@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/im.imageset/im@2x.png b/Passepartout/App/Flags.xcassets/flags/im.imageset/im@2x.png deleted file mode 100644 index 36ffa931..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/im.imageset/im@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/im.imageset/im@3x.png b/Passepartout/App/Flags.xcassets/flags/im.imageset/im@3x.png deleted file mode 100644 index e615defe..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/im.imageset/im@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/in.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/in.imageset/Contents.json deleted file mode 100644 index 02a5763e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/in.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "in@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "in@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/in.imageset/in@2x.png b/Passepartout/App/Flags.xcassets/flags/in.imageset/in@2x.png deleted file mode 100644 index 24c75e3d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/in.imageset/in@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/in.imageset/in@3x.png b/Passepartout/App/Flags.xcassets/flags/in.imageset/in@3x.png deleted file mode 100644 index c04592eb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/in.imageset/in@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/io.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/io.imageset/Contents.json deleted file mode 100644 index ab461d7c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/io.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "io@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "io@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/io.imageset/io@2x.png b/Passepartout/App/Flags.xcassets/flags/io.imageset/io@2x.png deleted file mode 100644 index 05147bca..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/io.imageset/io@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/io.imageset/io@3x.png b/Passepartout/App/Flags.xcassets/flags/io.imageset/io@3x.png deleted file mode 100644 index 539c3819..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/io.imageset/io@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/iq.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/iq.imageset/Contents.json deleted file mode 100644 index c7c87454..00000000 --- a/Passepartout/App/Flags.xcassets/flags/iq.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "iq@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "iq@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/iq.imageset/iq@2x.png b/Passepartout/App/Flags.xcassets/flags/iq.imageset/iq@2x.png deleted file mode 100644 index 1c026a28..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/iq.imageset/iq@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/iq.imageset/iq@3x.png b/Passepartout/App/Flags.xcassets/flags/iq.imageset/iq@3x.png deleted file mode 100644 index fbd675c6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/iq.imageset/iq@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ir.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ir.imageset/Contents.json deleted file mode 100644 index 9598ddc5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ir.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ir@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ir@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ir.imageset/ir@2x.png b/Passepartout/App/Flags.xcassets/flags/ir.imageset/ir@2x.png deleted file mode 100644 index b3aa87ea..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ir.imageset/ir@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ir.imageset/ir@3x.png b/Passepartout/App/Flags.xcassets/flags/ir.imageset/ir@3x.png deleted file mode 100644 index 8bc80468..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ir.imageset/ir@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/is.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/is.imageset/Contents.json deleted file mode 100644 index 3ccfe7ce..00000000 --- a/Passepartout/App/Flags.xcassets/flags/is.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "is@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "is@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/is.imageset/is@2x.png b/Passepartout/App/Flags.xcassets/flags/is.imageset/is@2x.png deleted file mode 100644 index dcf9fec7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/is.imageset/is@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/is.imageset/is@3x.png b/Passepartout/App/Flags.xcassets/flags/is.imageset/is@3x.png deleted file mode 100644 index aac10c63..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/is.imageset/is@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/it.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/it.imageset/Contents.json deleted file mode 100644 index c59fcb30..00000000 --- a/Passepartout/App/Flags.xcassets/flags/it.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "it@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "it@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/it.imageset/it@2x.png b/Passepartout/App/Flags.xcassets/flags/it.imageset/it@2x.png deleted file mode 100644 index b6474707..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/it.imageset/it@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/it.imageset/it@3x.png b/Passepartout/App/Flags.xcassets/flags/it.imageset/it@3x.png deleted file mode 100644 index 40b77c71..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/it.imageset/it@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/je.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/je.imageset/Contents.json deleted file mode 100644 index 2ce97cf8..00000000 --- a/Passepartout/App/Flags.xcassets/flags/je.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "je@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "je@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/je.imageset/je@2x.png b/Passepartout/App/Flags.xcassets/flags/je.imageset/je@2x.png deleted file mode 100644 index 2d36172f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/je.imageset/je@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/je.imageset/je@3x.png b/Passepartout/App/Flags.xcassets/flags/je.imageset/je@3x.png deleted file mode 100644 index 62d41000..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/je.imageset/je@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/jm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/jm.imageset/Contents.json deleted file mode 100644 index cc6fcf90..00000000 --- a/Passepartout/App/Flags.xcassets/flags/jm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "jm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "jm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/jm.imageset/jm@2x.png b/Passepartout/App/Flags.xcassets/flags/jm.imageset/jm@2x.png deleted file mode 100644 index 65736807..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/jm.imageset/jm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/jm.imageset/jm@3x.png b/Passepartout/App/Flags.xcassets/flags/jm.imageset/jm@3x.png deleted file mode 100644 index 08c3d951..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/jm.imageset/jm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/jo.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/jo.imageset/Contents.json deleted file mode 100644 index 198727e0..00000000 --- a/Passepartout/App/Flags.xcassets/flags/jo.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "jo@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "jo@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/jo.imageset/jo@2x.png b/Passepartout/App/Flags.xcassets/flags/jo.imageset/jo@2x.png deleted file mode 100644 index 37638f34..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/jo.imageset/jo@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/jo.imageset/jo@3x.png b/Passepartout/App/Flags.xcassets/flags/jo.imageset/jo@3x.png deleted file mode 100644 index 2c0efe7d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/jo.imageset/jo@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/jp.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/jp.imageset/Contents.json deleted file mode 100644 index 7ed4886c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/jp.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "jp@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "jp@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/jp.imageset/jp@2x.png b/Passepartout/App/Flags.xcassets/flags/jp.imageset/jp@2x.png deleted file mode 100644 index 4fdd7194..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/jp.imageset/jp@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/jp.imageset/jp@3x.png b/Passepartout/App/Flags.xcassets/flags/jp.imageset/jp@3x.png deleted file mode 100644 index ae3226d3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/jp.imageset/jp@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ke.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ke.imageset/Contents.json deleted file mode 100644 index 68a11284..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ke.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ke@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ke@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ke.imageset/ke@2x.png b/Passepartout/App/Flags.xcassets/flags/ke.imageset/ke@2x.png deleted file mode 100644 index a9cbc605..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ke.imageset/ke@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ke.imageset/ke@3x.png b/Passepartout/App/Flags.xcassets/flags/ke.imageset/ke@3x.png deleted file mode 100644 index 550e8eee..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ke.imageset/ke@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kg.imageset/Contents.json deleted file mode 100644 index 10ed8881..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kg.imageset/kg@2x.png b/Passepartout/App/Flags.xcassets/flags/kg.imageset/kg@2x.png deleted file mode 100644 index 6fc03cb5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kg.imageset/kg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kg.imageset/kg@3x.png b/Passepartout/App/Flags.xcassets/flags/kg.imageset/kg@3x.png deleted file mode 100644 index b660685e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kg.imageset/kg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kh.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kh.imageset/Contents.json deleted file mode 100644 index 423ac3ac..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kh.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kh@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kh@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kh.imageset/kh@2x.png b/Passepartout/App/Flags.xcassets/flags/kh.imageset/kh@2x.png deleted file mode 100644 index fb237415..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kh.imageset/kh@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kh.imageset/kh@3x.png b/Passepartout/App/Flags.xcassets/flags/kh.imageset/kh@3x.png deleted file mode 100644 index 1f88cfe1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kh.imageset/kh@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ki.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ki.imageset/Contents.json deleted file mode 100644 index 103a7f8e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ki.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ki@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ki@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ki.imageset/ki@2x.png b/Passepartout/App/Flags.xcassets/flags/ki.imageset/ki@2x.png deleted file mode 100644 index 5bbb48e6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ki.imageset/ki@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ki.imageset/ki@3x.png b/Passepartout/App/Flags.xcassets/flags/ki.imageset/ki@3x.png deleted file mode 100644 index 655d2db4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ki.imageset/ki@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/km.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/km.imageset/Contents.json deleted file mode 100644 index 4b603c3c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/km.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "km@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "km@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/km.imageset/km@2x.png b/Passepartout/App/Flags.xcassets/flags/km.imageset/km@2x.png deleted file mode 100644 index 4b2090ea..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/km.imageset/km@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/km.imageset/km@3x.png b/Passepartout/App/Flags.xcassets/flags/km.imageset/km@3x.png deleted file mode 100644 index 54afb8bb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/km.imageset/km@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kn.imageset/Contents.json deleted file mode 100644 index 782f11b6..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kn.imageset/kn@2x.png b/Passepartout/App/Flags.xcassets/flags/kn.imageset/kn@2x.png deleted file mode 100644 index 2417d34d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kn.imageset/kn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kn.imageset/kn@3x.png b/Passepartout/App/Flags.xcassets/flags/kn.imageset/kn@3x.png deleted file mode 100644 index 717af5bb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kn.imageset/kn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kp.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kp.imageset/Contents.json deleted file mode 100644 index dc604660..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kp.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kp@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kp@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kp.imageset/kp@2x.png b/Passepartout/App/Flags.xcassets/flags/kp.imageset/kp@2x.png deleted file mode 100644 index a0c3d43f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kp.imageset/kp@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kp.imageset/kp@3x.png b/Passepartout/App/Flags.xcassets/flags/kp.imageset/kp@3x.png deleted file mode 100644 index dc1a49b3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kp.imageset/kp@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kr.imageset/Contents.json deleted file mode 100644 index e51c6f44..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kr.imageset/kr@2x.png b/Passepartout/App/Flags.xcassets/flags/kr.imageset/kr@2x.png deleted file mode 100644 index ed15435c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kr.imageset/kr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kr.imageset/kr@3x.png b/Passepartout/App/Flags.xcassets/flags/kr.imageset/kr@3x.png deleted file mode 100644 index e8b3885c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kr.imageset/kr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kw.imageset/Contents.json deleted file mode 100644 index 7dd61ad6..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kw.imageset/kw@2x.png b/Passepartout/App/Flags.xcassets/flags/kw.imageset/kw@2x.png deleted file mode 100644 index 2cd9e7ab..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kw.imageset/kw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kw.imageset/kw@3x.png b/Passepartout/App/Flags.xcassets/flags/kw.imageset/kw@3x.png deleted file mode 100644 index a771c7f5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kw.imageset/kw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ky.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ky.imageset/Contents.json deleted file mode 100644 index 4a4160cf..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ky.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ky@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ky@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ky.imageset/ky@2x.png b/Passepartout/App/Flags.xcassets/flags/ky.imageset/ky@2x.png deleted file mode 100644 index 709da2de..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ky.imageset/ky@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ky.imageset/ky@3x.png b/Passepartout/App/Flags.xcassets/flags/ky.imageset/ky@3x.png deleted file mode 100644 index 645806f9..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ky.imageset/ky@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/kz.imageset/Contents.json deleted file mode 100644 index 75065861..00000000 --- a/Passepartout/App/Flags.xcassets/flags/kz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "kz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "kz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/kz.imageset/kz@2x.png b/Passepartout/App/Flags.xcassets/flags/kz.imageset/kz@2x.png deleted file mode 100644 index bf4028af..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kz.imageset/kz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/kz.imageset/kz@3x.png b/Passepartout/App/Flags.xcassets/flags/kz.imageset/kz@3x.png deleted file mode 100644 index ca4091e0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/kz.imageset/kz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/la.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/la.imageset/Contents.json deleted file mode 100644 index d0729f43..00000000 --- a/Passepartout/App/Flags.xcassets/flags/la.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "la@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "la@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/la.imageset/la@2x.png b/Passepartout/App/Flags.xcassets/flags/la.imageset/la@2x.png deleted file mode 100644 index a54d8101..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/la.imageset/la@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/la.imageset/la@3x.png b/Passepartout/App/Flags.xcassets/flags/la.imageset/la@3x.png deleted file mode 100644 index 0b30db5e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/la.imageset/la@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lb.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lb.imageset/Contents.json deleted file mode 100644 index 195bbb5c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lb.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lb@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lb@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lb.imageset/lb@2x.png b/Passepartout/App/Flags.xcassets/flags/lb.imageset/lb@2x.png deleted file mode 100644 index 72cd1f67..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lb.imageset/lb@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lb.imageset/lb@3x.png b/Passepartout/App/Flags.xcassets/flags/lb.imageset/lb@3x.png deleted file mode 100644 index 6d4b7096..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lb.imageset/lb@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lc.imageset/Contents.json deleted file mode 100644 index 7712a0f2..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lc.imageset/lc@2x.png b/Passepartout/App/Flags.xcassets/flags/lc.imageset/lc@2x.png deleted file mode 100644 index b90c9d26..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lc.imageset/lc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lc.imageset/lc@3x.png b/Passepartout/App/Flags.xcassets/flags/lc.imageset/lc@3x.png deleted file mode 100644 index 2b45b67c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lc.imageset/lc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/li.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/li.imageset/Contents.json deleted file mode 100644 index d792d4df..00000000 --- a/Passepartout/App/Flags.xcassets/flags/li.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "li@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "li@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/li.imageset/li@2x.png b/Passepartout/App/Flags.xcassets/flags/li.imageset/li@2x.png deleted file mode 100644 index ea6c726d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/li.imageset/li@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/li.imageset/li@3x.png b/Passepartout/App/Flags.xcassets/flags/li.imageset/li@3x.png deleted file mode 100644 index cafd08d5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/li.imageset/li@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lk.imageset/Contents.json deleted file mode 100644 index 81f9ebf9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lk.imageset/lk@2x.png b/Passepartout/App/Flags.xcassets/flags/lk.imageset/lk@2x.png deleted file mode 100644 index 31d4eda3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lk.imageset/lk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lk.imageset/lk@3x.png b/Passepartout/App/Flags.xcassets/flags/lk.imageset/lk@3x.png deleted file mode 100644 index 46c13059..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lk.imageset/lk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lr.imageset/Contents.json deleted file mode 100644 index 38464552..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lr.imageset/lr@2x.png b/Passepartout/App/Flags.xcassets/flags/lr.imageset/lr@2x.png deleted file mode 100644 index 383bc916..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lr.imageset/lr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lr.imageset/lr@3x.png b/Passepartout/App/Flags.xcassets/flags/lr.imageset/lr@3x.png deleted file mode 100644 index 730fb79b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lr.imageset/lr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ls.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ls.imageset/Contents.json deleted file mode 100644 index 0a4350f0..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ls.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ls@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ls@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ls.imageset/ls@2x.png b/Passepartout/App/Flags.xcassets/flags/ls.imageset/ls@2x.png deleted file mode 100644 index 7b0a8255..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ls.imageset/ls@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ls.imageset/ls@3x.png b/Passepartout/App/Flags.xcassets/flags/ls.imageset/ls@3x.png deleted file mode 100644 index 53967c05..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ls.imageset/ls@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lt.imageset/Contents.json deleted file mode 100644 index 0a71075c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lt.imageset/lt@2x.png b/Passepartout/App/Flags.xcassets/flags/lt.imageset/lt@2x.png deleted file mode 100644 index 0139785f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lt.imageset/lt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lt.imageset/lt@3x.png b/Passepartout/App/Flags.xcassets/flags/lt.imageset/lt@3x.png deleted file mode 100644 index 73e19c8c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lt.imageset/lt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lu.imageset/Contents.json deleted file mode 100644 index 80e8f320..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lu.imageset/lu@2x.png b/Passepartout/App/Flags.xcassets/flags/lu.imageset/lu@2x.png deleted file mode 100644 index b4235f32..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lu.imageset/lu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lu.imageset/lu@3x.png b/Passepartout/App/Flags.xcassets/flags/lu.imageset/lu@3x.png deleted file mode 100644 index a1ac5b13..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lu.imageset/lu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lv.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/lv.imageset/Contents.json deleted file mode 100644 index 42677cf5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/lv.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "lv@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "lv@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/lv.imageset/lv@2x.png b/Passepartout/App/Flags.xcassets/flags/lv.imageset/lv@2x.png deleted file mode 100644 index dbe5cf56..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lv.imageset/lv@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/lv.imageset/lv@3x.png b/Passepartout/App/Flags.xcassets/flags/lv.imageset/lv@3x.png deleted file mode 100644 index 9bea54c2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/lv.imageset/lv@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ly.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ly.imageset/Contents.json deleted file mode 100644 index e91cb286..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ly.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ly@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ly@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ly.imageset/ly@2x.png b/Passepartout/App/Flags.xcassets/flags/ly.imageset/ly@2x.png deleted file mode 100644 index 46303dd4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ly.imageset/ly@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ly.imageset/ly@3x.png b/Passepartout/App/Flags.xcassets/flags/ly.imageset/ly@3x.png deleted file mode 100644 index a005c374..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ly.imageset/ly@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ma.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ma.imageset/Contents.json deleted file mode 100644 index 144333be..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ma.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ma@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ma@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ma.imageset/ma@2x.png b/Passepartout/App/Flags.xcassets/flags/ma.imageset/ma@2x.png deleted file mode 100644 index c3d431ab..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ma.imageset/ma@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ma.imageset/ma@3x.png b/Passepartout/App/Flags.xcassets/flags/ma.imageset/ma@3x.png deleted file mode 100644 index b7b2a0f3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ma.imageset/ma@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mc.imageset/Contents.json deleted file mode 100644 index 47971ff9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mc.imageset/mc@2x.png b/Passepartout/App/Flags.xcassets/flags/mc.imageset/mc@2x.png deleted file mode 100644 index f192fa70..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mc.imageset/mc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mc.imageset/mc@3x.png b/Passepartout/App/Flags.xcassets/flags/mc.imageset/mc@3x.png deleted file mode 100644 index 67ee65a2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mc.imageset/mc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/md.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/md.imageset/Contents.json deleted file mode 100644 index 76682709..00000000 --- a/Passepartout/App/Flags.xcassets/flags/md.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "md@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "md@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/md.imageset/md@2x.png b/Passepartout/App/Flags.xcassets/flags/md.imageset/md@2x.png deleted file mode 100644 index 6e29c190..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/md.imageset/md@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/md.imageset/md@3x.png b/Passepartout/App/Flags.xcassets/flags/md.imageset/md@3x.png deleted file mode 100644 index 212213e3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/md.imageset/md@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/me.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/me.imageset/Contents.json deleted file mode 100644 index 954d3c0d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/me.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "me@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "me@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/me.imageset/me@2x.png b/Passepartout/App/Flags.xcassets/flags/me.imageset/me@2x.png deleted file mode 100644 index 16dd95af..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/me.imageset/me@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/me.imageset/me@3x.png b/Passepartout/App/Flags.xcassets/flags/me.imageset/me@3x.png deleted file mode 100644 index cd11963c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/me.imageset/me@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mf.imageset/Contents.json deleted file mode 100644 index 84292268..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mf.imageset/mf@2x.png b/Passepartout/App/Flags.xcassets/flags/mf.imageset/mf@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mf.imageset/mf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mf.imageset/mf@3x.png b/Passepartout/App/Flags.xcassets/flags/mf.imageset/mf@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mf.imageset/mf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mg.imageset/Contents.json deleted file mode 100644 index d8ed83d5..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mg.imageset/mg@2x.png b/Passepartout/App/Flags.xcassets/flags/mg.imageset/mg@2x.png deleted file mode 100644 index 2cea71cc..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mg.imageset/mg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mg.imageset/mg@3x.png b/Passepartout/App/Flags.xcassets/flags/mg.imageset/mg@3x.png deleted file mode 100644 index 774b14cb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mg.imageset/mg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mh.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mh.imageset/Contents.json deleted file mode 100644 index 7c5b3788..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mh.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mh@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mh@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mh.imageset/mh@2x.png b/Passepartout/App/Flags.xcassets/flags/mh.imageset/mh@2x.png deleted file mode 100644 index bab7d3f2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mh.imageset/mh@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mh.imageset/mh@3x.png b/Passepartout/App/Flags.xcassets/flags/mh.imageset/mh@3x.png deleted file mode 100644 index 2d1bd679..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mh.imageset/mh@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mk.imageset/Contents.json deleted file mode 100644 index b5a72fa3..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mk.imageset/mk@2x.png b/Passepartout/App/Flags.xcassets/flags/mk.imageset/mk@2x.png deleted file mode 100644 index a2fc1a4a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mk.imageset/mk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mk.imageset/mk@3x.png b/Passepartout/App/Flags.xcassets/flags/mk.imageset/mk@3x.png deleted file mode 100644 index e72a268e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mk.imageset/mk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ml.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ml.imageset/Contents.json deleted file mode 100644 index 7b2cd541..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ml.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ml@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ml@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ml.imageset/ml@2x.png b/Passepartout/App/Flags.xcassets/flags/ml.imageset/ml@2x.png deleted file mode 100644 index c7d0a3c1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ml.imageset/ml@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ml.imageset/ml@3x.png b/Passepartout/App/Flags.xcassets/flags/ml.imageset/ml@3x.png deleted file mode 100644 index f23d0a80..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ml.imageset/ml@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mm.imageset/Contents.json deleted file mode 100644 index fac2fb73..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mm.imageset/mm@2x.png b/Passepartout/App/Flags.xcassets/flags/mm.imageset/mm@2x.png deleted file mode 100644 index 87800b92..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mm.imageset/mm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mm.imageset/mm@3x.png b/Passepartout/App/Flags.xcassets/flags/mm.imageset/mm@3x.png deleted file mode 100644 index 24e879c2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mm.imageset/mm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mn.imageset/Contents.json deleted file mode 100644 index 58b5b662..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mn.imageset/mn@2x.png b/Passepartout/App/Flags.xcassets/flags/mn.imageset/mn@2x.png deleted file mode 100644 index b5e74a26..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mn.imageset/mn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mn.imageset/mn@3x.png b/Passepartout/App/Flags.xcassets/flags/mn.imageset/mn@3x.png deleted file mode 100644 index 31153761..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mn.imageset/mn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mo.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mo.imageset/Contents.json deleted file mode 100644 index 6be1f045..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mo.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mo@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mo@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mo.imageset/mo@2x.png b/Passepartout/App/Flags.xcassets/flags/mo.imageset/mo@2x.png deleted file mode 100644 index 8ff564ab..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mo.imageset/mo@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mo.imageset/mo@3x.png b/Passepartout/App/Flags.xcassets/flags/mo.imageset/mo@3x.png deleted file mode 100644 index 417a6a9d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mo.imageset/mo@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mp.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mp.imageset/Contents.json deleted file mode 100644 index 5d0fc266..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mp.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mp@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mp@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mp.imageset/mp@2x.png b/Passepartout/App/Flags.xcassets/flags/mp.imageset/mp@2x.png deleted file mode 100644 index 8ff95160..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mp.imageset/mp@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mp.imageset/mp@3x.png b/Passepartout/App/Flags.xcassets/flags/mp.imageset/mp@3x.png deleted file mode 100644 index 33985442..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mp.imageset/mp@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mq.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mq.imageset/Contents.json deleted file mode 100644 index 2820787f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mq.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mq@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mq@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mq.imageset/mq@2x.png b/Passepartout/App/Flags.xcassets/flags/mq.imageset/mq@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mq.imageset/mq@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mq.imageset/mq@3x.png b/Passepartout/App/Flags.xcassets/flags/mq.imageset/mq@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mq.imageset/mq@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mr.imageset/Contents.json deleted file mode 100644 index 3a27dfde..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mr.imageset/mr@2x.png b/Passepartout/App/Flags.xcassets/flags/mr.imageset/mr@2x.png deleted file mode 100644 index a24bddc2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mr.imageset/mr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mr.imageset/mr@3x.png b/Passepartout/App/Flags.xcassets/flags/mr.imageset/mr@3x.png deleted file mode 100644 index 1b620479..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mr.imageset/mr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ms.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ms.imageset/Contents.json deleted file mode 100644 index 9da4de8e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ms.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ms@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ms@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ms.imageset/ms@2x.png b/Passepartout/App/Flags.xcassets/flags/ms.imageset/ms@2x.png deleted file mode 100644 index 9d353222..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ms.imageset/ms@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ms.imageset/ms@3x.png b/Passepartout/App/Flags.xcassets/flags/ms.imageset/ms@3x.png deleted file mode 100644 index 82bc50cb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ms.imageset/ms@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mt.imageset/Contents.json deleted file mode 100644 index e2c30591..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mt.imageset/mt@2x.png b/Passepartout/App/Flags.xcassets/flags/mt.imageset/mt@2x.png deleted file mode 100644 index 736df9ca..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mt.imageset/mt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mt.imageset/mt@3x.png b/Passepartout/App/Flags.xcassets/flags/mt.imageset/mt@3x.png deleted file mode 100644 index 82d74da5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mt.imageset/mt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mu.imageset/Contents.json deleted file mode 100644 index 5e2c7994..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mu.imageset/mu@2x.png b/Passepartout/App/Flags.xcassets/flags/mu.imageset/mu@2x.png deleted file mode 100644 index 680b1afa..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mu.imageset/mu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mu.imageset/mu@3x.png b/Passepartout/App/Flags.xcassets/flags/mu.imageset/mu@3x.png deleted file mode 100644 index e3b8ddb6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mu.imageset/mu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mv.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mv.imageset/Contents.json deleted file mode 100644 index 53757b3a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mv.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mv@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mv@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mv.imageset/mv@2x.png b/Passepartout/App/Flags.xcassets/flags/mv.imageset/mv@2x.png deleted file mode 100644 index 4eecfc7b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mv.imageset/mv@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mv.imageset/mv@3x.png b/Passepartout/App/Flags.xcassets/flags/mv.imageset/mv@3x.png deleted file mode 100644 index 9fc1a5a9..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mv.imageset/mv@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mw.imageset/Contents.json deleted file mode 100644 index 8d741139..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mw.imageset/mw@2x.png b/Passepartout/App/Flags.xcassets/flags/mw.imageset/mw@2x.png deleted file mode 100644 index 916ad434..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mw.imageset/mw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mw.imageset/mw@3x.png b/Passepartout/App/Flags.xcassets/flags/mw.imageset/mw@3x.png deleted file mode 100644 index fc2ecd2b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mw.imageset/mw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mx.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mx.imageset/Contents.json deleted file mode 100644 index 31970ec4..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mx.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mx@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mx@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mx.imageset/mx@2x.png b/Passepartout/App/Flags.xcassets/flags/mx.imageset/mx@2x.png deleted file mode 100644 index 5b3a75bd..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mx.imageset/mx@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mx.imageset/mx@3x.png b/Passepartout/App/Flags.xcassets/flags/mx.imageset/mx@3x.png deleted file mode 100644 index 015fa579..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mx.imageset/mx@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/my.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/my.imageset/Contents.json deleted file mode 100644 index 6e53f21b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/my.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "my@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "my@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/my.imageset/my@2x.png b/Passepartout/App/Flags.xcassets/flags/my.imageset/my@2x.png deleted file mode 100644 index b15464e8..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/my.imageset/my@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/my.imageset/my@3x.png b/Passepartout/App/Flags.xcassets/flags/my.imageset/my@3x.png deleted file mode 100644 index c6d7b3c1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/my.imageset/my@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/mz.imageset/Contents.json deleted file mode 100644 index 3b4d02c9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/mz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/mz.imageset/mz@2x.png b/Passepartout/App/Flags.xcassets/flags/mz.imageset/mz@2x.png deleted file mode 100644 index f6b695a6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mz.imageset/mz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/mz.imageset/mz@3x.png b/Passepartout/App/Flags.xcassets/flags/mz.imageset/mz@3x.png deleted file mode 100644 index 65c0a10a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/mz.imageset/mz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/na.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/na.imageset/Contents.json deleted file mode 100644 index 3b73c1eb..00000000 --- a/Passepartout/App/Flags.xcassets/flags/na.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "na@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "na@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/na.imageset/na@2x.png b/Passepartout/App/Flags.xcassets/flags/na.imageset/na@2x.png deleted file mode 100644 index add72116..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/na.imageset/na@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/na.imageset/na@3x.png b/Passepartout/App/Flags.xcassets/flags/na.imageset/na@3x.png deleted file mode 100644 index 855391d1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/na.imageset/na@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/nc.imageset/Contents.json deleted file mode 100644 index 9287c8b3..00000000 --- a/Passepartout/App/Flags.xcassets/flags/nc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "nc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/nc.imageset/nc@2x.png b/Passepartout/App/Flags.xcassets/flags/nc.imageset/nc@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nc.imageset/nc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nc.imageset/nc@3x.png b/Passepartout/App/Flags.xcassets/flags/nc.imageset/nc@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nc.imageset/nc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ne.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ne.imageset/Contents.json deleted file mode 100644 index 764ee262..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ne.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ne@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ne@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ne.imageset/ne@2x.png b/Passepartout/App/Flags.xcassets/flags/ne.imageset/ne@2x.png deleted file mode 100644 index 6fdf7420..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ne.imageset/ne@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ne.imageset/ne@3x.png b/Passepartout/App/Flags.xcassets/flags/ne.imageset/ne@3x.png deleted file mode 100644 index d0f88d17..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ne.imageset/ne@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/nf.imageset/Contents.json deleted file mode 100644 index 47a555ca..00000000 --- a/Passepartout/App/Flags.xcassets/flags/nf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "nf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/nf.imageset/nf@2x.png b/Passepartout/App/Flags.xcassets/flags/nf.imageset/nf@2x.png deleted file mode 100644 index a4fe186b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nf.imageset/nf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nf.imageset/nf@3x.png b/Passepartout/App/Flags.xcassets/flags/nf.imageset/nf@3x.png deleted file mode 100644 index e75e51ff..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nf.imageset/nf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ng.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ng.imageset/Contents.json deleted file mode 100644 index 497947a0..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ng.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ng@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ng@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ng.imageset/ng@2x.png b/Passepartout/App/Flags.xcassets/flags/ng.imageset/ng@2x.png deleted file mode 100644 index 601ae081..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ng.imageset/ng@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ng.imageset/ng@3x.png b/Passepartout/App/Flags.xcassets/flags/ng.imageset/ng@3x.png deleted file mode 100644 index 918bb92b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ng.imageset/ng@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ni.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ni.imageset/Contents.json deleted file mode 100644 index 70d1fe41..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ni.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ni@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ni@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ni.imageset/ni@2x.png b/Passepartout/App/Flags.xcassets/flags/ni.imageset/ni@2x.png deleted file mode 100644 index 00ab66fb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ni.imageset/ni@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ni.imageset/ni@3x.png b/Passepartout/App/Flags.xcassets/flags/ni.imageset/ni@3x.png deleted file mode 100644 index b53ec733..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ni.imageset/ni@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/nl.imageset/Contents.json deleted file mode 100644 index 2f73923d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/nl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "nl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/nl.imageset/nl@2x.png b/Passepartout/App/Flags.xcassets/flags/nl.imageset/nl@2x.png deleted file mode 100644 index 4f631c54..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nl.imageset/nl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nl.imageset/nl@3x.png b/Passepartout/App/Flags.xcassets/flags/nl.imageset/nl@3x.png deleted file mode 100644 index c6feb388..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nl.imageset/nl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/no.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/no.imageset/Contents.json deleted file mode 100644 index 04face2b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/no.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "no@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "no@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/no.imageset/no@2x.png b/Passepartout/App/Flags.xcassets/flags/no.imageset/no@2x.png deleted file mode 100644 index 15ab3128..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/no.imageset/no@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/no.imageset/no@3x.png b/Passepartout/App/Flags.xcassets/flags/no.imageset/no@3x.png deleted file mode 100644 index d4a9a7ca..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/no.imageset/no@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/np.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/np.imageset/Contents.json deleted file mode 100644 index 1757ac7d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/np.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "np@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "np@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/np.imageset/np@2x.png b/Passepartout/App/Flags.xcassets/flags/np.imageset/np@2x.png deleted file mode 100644 index 870c764e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/np.imageset/np@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/np.imageset/np@3x.png b/Passepartout/App/Flags.xcassets/flags/np.imageset/np@3x.png deleted file mode 100644 index 9d83c412..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/np.imageset/np@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/nr.imageset/Contents.json deleted file mode 100644 index 0f55378b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/nr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "nr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/nr.imageset/nr@2x.png b/Passepartout/App/Flags.xcassets/flags/nr.imageset/nr@2x.png deleted file mode 100644 index dc1401a7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nr.imageset/nr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nr.imageset/nr@3x.png b/Passepartout/App/Flags.xcassets/flags/nr.imageset/nr@3x.png deleted file mode 100644 index 9e976b60..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nr.imageset/nr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/nu.imageset/Contents.json deleted file mode 100644 index 6ca17282..00000000 --- a/Passepartout/App/Flags.xcassets/flags/nu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "nu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/nu.imageset/nu@2x.png b/Passepartout/App/Flags.xcassets/flags/nu.imageset/nu@2x.png deleted file mode 100644 index 686a1d26..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nu.imageset/nu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nu.imageset/nu@3x.png b/Passepartout/App/Flags.xcassets/flags/nu.imageset/nu@3x.png deleted file mode 100644 index af6d1025..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nu.imageset/nu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/nz.imageset/Contents.json deleted file mode 100644 index 3b318786..00000000 --- a/Passepartout/App/Flags.xcassets/flags/nz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "nz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/nz.imageset/nz@2x.png b/Passepartout/App/Flags.xcassets/flags/nz.imageset/nz@2x.png deleted file mode 100644 index ba97c978..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nz.imageset/nz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/nz.imageset/nz@3x.png b/Passepartout/App/Flags.xcassets/flags/nz.imageset/nz@3x.png deleted file mode 100644 index df733a1e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/nz.imageset/nz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/om.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/om.imageset/Contents.json deleted file mode 100644 index 10fafd75..00000000 --- a/Passepartout/App/Flags.xcassets/flags/om.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "om@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "om@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/om.imageset/om@2x.png b/Passepartout/App/Flags.xcassets/flags/om.imageset/om@2x.png deleted file mode 100644 index 9736af00..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/om.imageset/om@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/om.imageset/om@3x.png b/Passepartout/App/Flags.xcassets/flags/om.imageset/om@3x.png deleted file mode 100644 index cc4ee393..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/om.imageset/om@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pa.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pa.imageset/Contents.json deleted file mode 100644 index 37cc6cd3..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pa.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pa@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pa@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pa.imageset/pa@2x.png b/Passepartout/App/Flags.xcassets/flags/pa.imageset/pa@2x.png deleted file mode 100644 index 59cce4f7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pa.imageset/pa@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pa.imageset/pa@3x.png b/Passepartout/App/Flags.xcassets/flags/pa.imageset/pa@3x.png deleted file mode 100644 index 1d92c8d1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pa.imageset/pa@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pe.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pe.imageset/Contents.json deleted file mode 100644 index abba4a21..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pe.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pe@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pe@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pe.imageset/pe@2x.png b/Passepartout/App/Flags.xcassets/flags/pe.imageset/pe@2x.png deleted file mode 100644 index 4599d72f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pe.imageset/pe@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pe.imageset/pe@3x.png b/Passepartout/App/Flags.xcassets/flags/pe.imageset/pe@3x.png deleted file mode 100644 index 8cd8686c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pe.imageset/pe@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pf.imageset/Contents.json deleted file mode 100644 index f258944a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pf.imageset/pf@2x.png b/Passepartout/App/Flags.xcassets/flags/pf.imageset/pf@2x.png deleted file mode 100644 index 861a5b3a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pf.imageset/pf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pf.imageset/pf@3x.png b/Passepartout/App/Flags.xcassets/flags/pf.imageset/pf@3x.png deleted file mode 100644 index 65b61e67..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pf.imageset/pf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pg.imageset/Contents.json deleted file mode 100644 index c69d9730..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pg.imageset/pg@2x.png b/Passepartout/App/Flags.xcassets/flags/pg.imageset/pg@2x.png deleted file mode 100644 index 428d5b4c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pg.imageset/pg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pg.imageset/pg@3x.png b/Passepartout/App/Flags.xcassets/flags/pg.imageset/pg@3x.png deleted file mode 100644 index a10b19de..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pg.imageset/pg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ph.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ph.imageset/Contents.json deleted file mode 100644 index 2ea988f2..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ph.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ph@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ph@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ph.imageset/ph@2x.png b/Passepartout/App/Flags.xcassets/flags/ph.imageset/ph@2x.png deleted file mode 100644 index 6523ce7a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ph.imageset/ph@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ph.imageset/ph@3x.png b/Passepartout/App/Flags.xcassets/flags/ph.imageset/ph@3x.png deleted file mode 100644 index 10fa7ab0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ph.imageset/ph@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pk.imageset/Contents.json deleted file mode 100644 index 44615c4a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pk.imageset/pk@2x.png b/Passepartout/App/Flags.xcassets/flags/pk.imageset/pk@2x.png deleted file mode 100644 index c81c8e4d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pk.imageset/pk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pk.imageset/pk@3x.png b/Passepartout/App/Flags.xcassets/flags/pk.imageset/pk@3x.png deleted file mode 100644 index 10e7cfce..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pk.imageset/pk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pl.imageset/Contents.json deleted file mode 100644 index e50e8cc0..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pl.imageset/pl@2x.png b/Passepartout/App/Flags.xcassets/flags/pl.imageset/pl@2x.png deleted file mode 100644 index 690735b7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pl.imageset/pl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pl.imageset/pl@3x.png b/Passepartout/App/Flags.xcassets/flags/pl.imageset/pl@3x.png deleted file mode 100644 index d894aebb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pl.imageset/pl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pm.imageset/Contents.json deleted file mode 100644 index be2e1130..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pm.imageset/pm@2x.png b/Passepartout/App/Flags.xcassets/flags/pm.imageset/pm@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pm.imageset/pm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pm.imageset/pm@3x.png b/Passepartout/App/Flags.xcassets/flags/pm.imageset/pm@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pm.imageset/pm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pn.imageset/Contents.json deleted file mode 100644 index fe607877..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pn.imageset/pn@2x.png b/Passepartout/App/Flags.xcassets/flags/pn.imageset/pn@2x.png deleted file mode 100644 index 6727f938..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pn.imageset/pn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pn.imageset/pn@3x.png b/Passepartout/App/Flags.xcassets/flags/pn.imageset/pn@3x.png deleted file mode 100644 index 1ac177ff..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pn.imageset/pn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pr.imageset/Contents.json deleted file mode 100644 index 4073c21e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pr.imageset/pr@2x.png b/Passepartout/App/Flags.xcassets/flags/pr.imageset/pr@2x.png deleted file mode 100644 index a6fe9cc1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pr.imageset/pr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pr.imageset/pr@3x.png b/Passepartout/App/Flags.xcassets/flags/pr.imageset/pr@3x.png deleted file mode 100644 index 4c44c8df..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pr.imageset/pr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ps.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ps.imageset/Contents.json deleted file mode 100644 index 74da65e7..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ps.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ps@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ps@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ps.imageset/ps@2x.png b/Passepartout/App/Flags.xcassets/flags/ps.imageset/ps@2x.png deleted file mode 100644 index 5075ee6a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ps.imageset/ps@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ps.imageset/ps@3x.png b/Passepartout/App/Flags.xcassets/flags/ps.imageset/ps@3x.png deleted file mode 100644 index 237e60ec..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ps.imageset/ps@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pt.imageset/Contents.json deleted file mode 100644 index fd5b9cad..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pt.imageset/pt@2x.png b/Passepartout/App/Flags.xcassets/flags/pt.imageset/pt@2x.png deleted file mode 100644 index 9f0f606d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pt.imageset/pt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pt.imageset/pt@3x.png b/Passepartout/App/Flags.xcassets/flags/pt.imageset/pt@3x.png deleted file mode 100644 index e4072f18..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pt.imageset/pt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/pw.imageset/Contents.json deleted file mode 100644 index 14f31b4b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/pw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/pw.imageset/pw@2x.png b/Passepartout/App/Flags.xcassets/flags/pw.imageset/pw@2x.png deleted file mode 100644 index 2eddd77f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pw.imageset/pw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/pw.imageset/pw@3x.png b/Passepartout/App/Flags.xcassets/flags/pw.imageset/pw@3x.png deleted file mode 100644 index d382e1da..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/pw.imageset/pw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/py.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/py.imageset/Contents.json deleted file mode 100644 index e430edbf..00000000 --- a/Passepartout/App/Flags.xcassets/flags/py.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "py@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "py@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/py.imageset/py@2x.png b/Passepartout/App/Flags.xcassets/flags/py.imageset/py@2x.png deleted file mode 100644 index 3e88d425..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/py.imageset/py@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/py.imageset/py@3x.png b/Passepartout/App/Flags.xcassets/flags/py.imageset/py@3x.png deleted file mode 100644 index 1a0f1fb2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/py.imageset/py@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/qa.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/qa.imageset/Contents.json deleted file mode 100644 index 3ca2d93f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/qa.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "qa@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "qa@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/qa.imageset/qa@2x.png b/Passepartout/App/Flags.xcassets/flags/qa.imageset/qa@2x.png deleted file mode 100644 index 488d510c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/qa.imageset/qa@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/qa.imageset/qa@3x.png b/Passepartout/App/Flags.xcassets/flags/qa.imageset/qa@3x.png deleted file mode 100644 index 6cb40e8b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/qa.imageset/qa@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/re.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/re.imageset/Contents.json deleted file mode 100644 index 83133245..00000000 --- a/Passepartout/App/Flags.xcassets/flags/re.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "re@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "re@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/re.imageset/re@2x.png b/Passepartout/App/Flags.xcassets/flags/re.imageset/re@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/re.imageset/re@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/re.imageset/re@3x.png b/Passepartout/App/Flags.xcassets/flags/re.imageset/re@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/re.imageset/re@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ro.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ro.imageset/Contents.json deleted file mode 100644 index 2853e8db..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ro.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ro@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ro@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ro.imageset/ro@2x.png b/Passepartout/App/Flags.xcassets/flags/ro.imageset/ro@2x.png deleted file mode 100644 index b8599719..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ro.imageset/ro@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ro.imageset/ro@3x.png b/Passepartout/App/Flags.xcassets/flags/ro.imageset/ro@3x.png deleted file mode 100644 index 7a04d6f7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ro.imageset/ro@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/rs.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/rs.imageset/Contents.json deleted file mode 100644 index 393bbfe4..00000000 --- a/Passepartout/App/Flags.xcassets/flags/rs.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "rs@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "rs@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/rs.imageset/rs@2x.png b/Passepartout/App/Flags.xcassets/flags/rs.imageset/rs@2x.png deleted file mode 100644 index dd3a58ac..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/rs.imageset/rs@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/rs.imageset/rs@3x.png b/Passepartout/App/Flags.xcassets/flags/rs.imageset/rs@3x.png deleted file mode 100644 index 63b93053..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/rs.imageset/rs@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ru.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ru.imageset/Contents.json deleted file mode 100644 index ba5226c9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ru.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ru@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ru@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ru.imageset/ru@2x.png b/Passepartout/App/Flags.xcassets/flags/ru.imageset/ru@2x.png deleted file mode 100644 index 46b020ae..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ru.imageset/ru@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ru.imageset/ru@3x.png b/Passepartout/App/Flags.xcassets/flags/ru.imageset/ru@3x.png deleted file mode 100644 index 6e215de3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ru.imageset/ru@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/rw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/rw.imageset/Contents.json deleted file mode 100644 index 2ee6c9dd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/rw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "rw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "rw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/rw.imageset/rw@2x.png b/Passepartout/App/Flags.xcassets/flags/rw.imageset/rw@2x.png deleted file mode 100644 index 755af68b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/rw.imageset/rw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/rw.imageset/rw@3x.png b/Passepartout/App/Flags.xcassets/flags/rw.imageset/rw@3x.png deleted file mode 100644 index 8daedaa0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/rw.imageset/rw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sa.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sa.imageset/Contents.json deleted file mode 100644 index f6a16107..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sa.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sa@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sa@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sa.imageset/sa@2x.png b/Passepartout/App/Flags.xcassets/flags/sa.imageset/sa@2x.png deleted file mode 100644 index d3124fae..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sa.imageset/sa@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sa.imageset/sa@3x.png b/Passepartout/App/Flags.xcassets/flags/sa.imageset/sa@3x.png deleted file mode 100644 index da2ff59f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sa.imageset/sa@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sb.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sb.imageset/Contents.json deleted file mode 100644 index 3dc5434c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sb.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sb@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sb@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sb.imageset/sb@2x.png b/Passepartout/App/Flags.xcassets/flags/sb.imageset/sb@2x.png deleted file mode 100644 index f16f0f3a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sb.imageset/sb@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sb.imageset/sb@3x.png b/Passepartout/App/Flags.xcassets/flags/sb.imageset/sb@3x.png deleted file mode 100644 index 2ff9ca7c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sb.imageset/sb@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sc.imageset/Contents.json deleted file mode 100644 index 282bd27e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sc.imageset/sc@2x.png b/Passepartout/App/Flags.xcassets/flags/sc.imageset/sc@2x.png deleted file mode 100644 index 018db692..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sc.imageset/sc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sc.imageset/sc@3x.png b/Passepartout/App/Flags.xcassets/flags/sc.imageset/sc@3x.png deleted file mode 100644 index 8469e6b5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sc.imageset/sc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sd.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sd.imageset/Contents.json deleted file mode 100644 index e790510e..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sd.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sd@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sd@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sd.imageset/sd@2x.png b/Passepartout/App/Flags.xcassets/flags/sd.imageset/sd@2x.png deleted file mode 100644 index 553cb068..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sd.imageset/sd@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sd.imageset/sd@3x.png b/Passepartout/App/Flags.xcassets/flags/sd.imageset/sd@3x.png deleted file mode 100644 index 6b24de69..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sd.imageset/sd@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/se.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/se.imageset/Contents.json deleted file mode 100644 index 168d11cd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/se.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "se@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "se@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/se.imageset/se@2x.png b/Passepartout/App/Flags.xcassets/flags/se.imageset/se@2x.png deleted file mode 100644 index 0d5a0dca..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/se.imageset/se@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/se.imageset/se@3x.png b/Passepartout/App/Flags.xcassets/flags/se.imageset/se@3x.png deleted file mode 100644 index 49839b8a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/se.imageset/se@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sg.imageset/Contents.json deleted file mode 100644 index d251c0f2..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sg.imageset/sg@2x.png b/Passepartout/App/Flags.xcassets/flags/sg.imageset/sg@2x.png deleted file mode 100644 index 4a1bbe43..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sg.imageset/sg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sg.imageset/sg@3x.png b/Passepartout/App/Flags.xcassets/flags/sg.imageset/sg@3x.png deleted file mode 100644 index 7e26a8f2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sg.imageset/sg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sh.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sh.imageset/Contents.json deleted file mode 100644 index 811d8a03..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sh.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sh@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sh@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sh.imageset/sh@2x.png b/Passepartout/App/Flags.xcassets/flags/sh.imageset/sh@2x.png deleted file mode 100644 index e5ecd835..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sh.imageset/sh@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sh.imageset/sh@3x.png b/Passepartout/App/Flags.xcassets/flags/sh.imageset/sh@3x.png deleted file mode 100644 index 8d8cd38f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sh.imageset/sh@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/si.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/si.imageset/Contents.json deleted file mode 100644 index 2cffdcae..00000000 --- a/Passepartout/App/Flags.xcassets/flags/si.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "si@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "si@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/si.imageset/si@2x.png b/Passepartout/App/Flags.xcassets/flags/si.imageset/si@2x.png deleted file mode 100644 index 1eb2a473..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/si.imageset/si@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/si.imageset/si@3x.png b/Passepartout/App/Flags.xcassets/flags/si.imageset/si@3x.png deleted file mode 100644 index 0f5d6afb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/si.imageset/si@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sj.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sj.imageset/Contents.json deleted file mode 100644 index b77353e9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sj.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sj@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sj@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sj.imageset/sj@2x.png b/Passepartout/App/Flags.xcassets/flags/sj.imageset/sj@2x.png deleted file mode 100644 index ff0faeef..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sj.imageset/sj@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sj.imageset/sj@3x.png b/Passepartout/App/Flags.xcassets/flags/sj.imageset/sj@3x.png deleted file mode 100644 index ce3339d7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sj.imageset/sj@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sk.imageset/Contents.json deleted file mode 100644 index 841f7305..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sk.imageset/sk@2x.png b/Passepartout/App/Flags.xcassets/flags/sk.imageset/sk@2x.png deleted file mode 100644 index 7d2308b5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sk.imageset/sk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sk.imageset/sk@3x.png b/Passepartout/App/Flags.xcassets/flags/sk.imageset/sk@3x.png deleted file mode 100644 index b9499a58..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sk.imageset/sk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sl.imageset/Contents.json deleted file mode 100644 index d10c6c5a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sl.imageset/sl@2x.png b/Passepartout/App/Flags.xcassets/flags/sl.imageset/sl@2x.png deleted file mode 100644 index 46c39889..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sl.imageset/sl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sl.imageset/sl@3x.png b/Passepartout/App/Flags.xcassets/flags/sl.imageset/sl@3x.png deleted file mode 100644 index e2860d75..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sl.imageset/sl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sm.imageset/Contents.json deleted file mode 100644 index 7b0e3341..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sm.imageset/sm@2x.png b/Passepartout/App/Flags.xcassets/flags/sm.imageset/sm@2x.png deleted file mode 100644 index b2d0ef22..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sm.imageset/sm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sm.imageset/sm@3x.png b/Passepartout/App/Flags.xcassets/flags/sm.imageset/sm@3x.png deleted file mode 100644 index 19cac0c5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sm.imageset/sm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sn.imageset/Contents.json deleted file mode 100644 index f4b5a049..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sn.imageset/sn@2x.png b/Passepartout/App/Flags.xcassets/flags/sn.imageset/sn@2x.png deleted file mode 100644 index 7d88b4b1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sn.imageset/sn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sn.imageset/sn@3x.png b/Passepartout/App/Flags.xcassets/flags/sn.imageset/sn@3x.png deleted file mode 100644 index 4deb1ba6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sn.imageset/sn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/so.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/so.imageset/Contents.json deleted file mode 100644 index 2fbafee1..00000000 --- a/Passepartout/App/Flags.xcassets/flags/so.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "so@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "so@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/so.imageset/so@2x.png b/Passepartout/App/Flags.xcassets/flags/so.imageset/so@2x.png deleted file mode 100644 index 4774480c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/so.imageset/so@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/so.imageset/so@3x.png b/Passepartout/App/Flags.xcassets/flags/so.imageset/so@3x.png deleted file mode 100644 index 892e649e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/so.imageset/so@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sr.imageset/Contents.json deleted file mode 100644 index 40255474..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sr.imageset/sr@2x.png b/Passepartout/App/Flags.xcassets/flags/sr.imageset/sr@2x.png deleted file mode 100644 index 5df33299..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sr.imageset/sr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sr.imageset/sr@3x.png b/Passepartout/App/Flags.xcassets/flags/sr.imageset/sr@3x.png deleted file mode 100644 index af77c309..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sr.imageset/sr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ss.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ss.imageset/Contents.json deleted file mode 100644 index b5a1e806..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ss.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ss@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ss@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ss.imageset/ss@2x.png b/Passepartout/App/Flags.xcassets/flags/ss.imageset/ss@2x.png deleted file mode 100644 index 492cd102..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ss.imageset/ss@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ss.imageset/ss@3x.png b/Passepartout/App/Flags.xcassets/flags/ss.imageset/ss@3x.png deleted file mode 100644 index 8f864501..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ss.imageset/ss@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/st.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/st.imageset/Contents.json deleted file mode 100644 index 4c900098..00000000 --- a/Passepartout/App/Flags.xcassets/flags/st.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "st@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "st@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/st.imageset/st@2x.png b/Passepartout/App/Flags.xcassets/flags/st.imageset/st@2x.png deleted file mode 100644 index e506a7f1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/st.imageset/st@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/st.imageset/st@3x.png b/Passepartout/App/Flags.xcassets/flags/st.imageset/st@3x.png deleted file mode 100644 index 688179fb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/st.imageset/st@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sv.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sv.imageset/Contents.json deleted file mode 100644 index bf1d63a6..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sv.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sv@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sv@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sv.imageset/sv@2x.png b/Passepartout/App/Flags.xcassets/flags/sv.imageset/sv@2x.png deleted file mode 100644 index f15bd3e7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sv.imageset/sv@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sv.imageset/sv@3x.png b/Passepartout/App/Flags.xcassets/flags/sv.imageset/sv@3x.png deleted file mode 100644 index 3c9a583e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sv.imageset/sv@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sx.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sx.imageset/Contents.json deleted file mode 100644 index a3a9ad97..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sx.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sx@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sx@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sx.imageset/sx@2x.png b/Passepartout/App/Flags.xcassets/flags/sx.imageset/sx@2x.png deleted file mode 100644 index 9b9fe8a0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sx.imageset/sx@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sx.imageset/sx@3x.png b/Passepartout/App/Flags.xcassets/flags/sx.imageset/sx@3x.png deleted file mode 100644 index 455ea5d1..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sx.imageset/sx@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sy.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sy.imageset/Contents.json deleted file mode 100644 index 9e97adfc..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sy.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sy@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sy@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sy.imageset/sy@2x.png b/Passepartout/App/Flags.xcassets/flags/sy.imageset/sy@2x.png deleted file mode 100644 index baddc404..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sy.imageset/sy@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sy.imageset/sy@3x.png b/Passepartout/App/Flags.xcassets/flags/sy.imageset/sy@3x.png deleted file mode 100644 index ffbd7fa6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sy.imageset/sy@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/sz.imageset/Contents.json deleted file mode 100644 index 1e8ba38a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/sz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "sz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "sz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/sz.imageset/sz@2x.png b/Passepartout/App/Flags.xcassets/flags/sz.imageset/sz@2x.png deleted file mode 100644 index 48ebcd01..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sz.imageset/sz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/sz.imageset/sz@3x.png b/Passepartout/App/Flags.xcassets/flags/sz.imageset/sz@3x.png deleted file mode 100644 index da2954a2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/sz.imageset/sz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tc.imageset/Contents.json deleted file mode 100644 index f051f128..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tc.imageset/tc@2x.png b/Passepartout/App/Flags.xcassets/flags/tc.imageset/tc@2x.png deleted file mode 100644 index 4415edb2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tc.imageset/tc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tc.imageset/tc@3x.png b/Passepartout/App/Flags.xcassets/flags/tc.imageset/tc@3x.png deleted file mode 100644 index b122fc0e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tc.imageset/tc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/td.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/td.imageset/Contents.json deleted file mode 100644 index 385ba7db..00000000 --- a/Passepartout/App/Flags.xcassets/flags/td.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "td@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "td@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/td.imageset/td@2x.png b/Passepartout/App/Flags.xcassets/flags/td.imageset/td@2x.png deleted file mode 100644 index 303c5606..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/td.imageset/td@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/td.imageset/td@3x.png b/Passepartout/App/Flags.xcassets/flags/td.imageset/td@3x.png deleted file mode 100644 index e2dfc824..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/td.imageset/td@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tf.imageset/Contents.json deleted file mode 100644 index ac0be50d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tf.imageset/tf@2x.png b/Passepartout/App/Flags.xcassets/flags/tf.imageset/tf@2x.png deleted file mode 100644 index 916b8ab9..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tf.imageset/tf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tf.imageset/tf@3x.png b/Passepartout/App/Flags.xcassets/flags/tf.imageset/tf@3x.png deleted file mode 100644 index bf8bb6c5..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tf.imageset/tf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tg.imageset/Contents.json deleted file mode 100644 index d3b40886..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tg.imageset/tg@2x.png b/Passepartout/App/Flags.xcassets/flags/tg.imageset/tg@2x.png deleted file mode 100644 index b6857fa4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tg.imageset/tg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tg.imageset/tg@3x.png b/Passepartout/App/Flags.xcassets/flags/tg.imageset/tg@3x.png deleted file mode 100644 index 97ca0446..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tg.imageset/tg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/th.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/th.imageset/Contents.json deleted file mode 100644 index dfe7a6ea..00000000 --- a/Passepartout/App/Flags.xcassets/flags/th.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "th@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "th@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/th.imageset/th@2x.png b/Passepartout/App/Flags.xcassets/flags/th.imageset/th@2x.png deleted file mode 100644 index 3a474239..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/th.imageset/th@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/th.imageset/th@3x.png b/Passepartout/App/Flags.xcassets/flags/th.imageset/th@3x.png deleted file mode 100644 index 55912d82..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/th.imageset/th@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tj.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tj.imageset/Contents.json deleted file mode 100644 index d215bf85..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tj.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tj@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tj@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tj.imageset/tj@2x.png b/Passepartout/App/Flags.xcassets/flags/tj.imageset/tj@2x.png deleted file mode 100644 index 66ba3f4e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tj.imageset/tj@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tj.imageset/tj@3x.png b/Passepartout/App/Flags.xcassets/flags/tj.imageset/tj@3x.png deleted file mode 100644 index 7bf23d95..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tj.imageset/tj@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tk.imageset/Contents.json deleted file mode 100644 index 71ffcd2f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tk.imageset/tk@2x.png b/Passepartout/App/Flags.xcassets/flags/tk.imageset/tk@2x.png deleted file mode 100644 index eaad6d4b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tk.imageset/tk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tk.imageset/tk@3x.png b/Passepartout/App/Flags.xcassets/flags/tk.imageset/tk@3x.png deleted file mode 100644 index b348d5e0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tk.imageset/tk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tl.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tl.imageset/Contents.json deleted file mode 100644 index 07c4d944..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tl.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tl@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tl@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tl.imageset/tl@2x.png b/Passepartout/App/Flags.xcassets/flags/tl.imageset/tl@2x.png deleted file mode 100644 index f9359717..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tl.imageset/tl@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tl.imageset/tl@3x.png b/Passepartout/App/Flags.xcassets/flags/tl.imageset/tl@3x.png deleted file mode 100644 index 6fd9cf6e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tl.imageset/tl@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tm.imageset/Contents.json deleted file mode 100644 index 23cb8c9b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tm.imageset/tm@2x.png b/Passepartout/App/Flags.xcassets/flags/tm.imageset/tm@2x.png deleted file mode 100644 index 3aa86d0b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tm.imageset/tm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tm.imageset/tm@3x.png b/Passepartout/App/Flags.xcassets/flags/tm.imageset/tm@3x.png deleted file mode 100644 index 09d70fc0..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tm.imageset/tm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tn.imageset/Contents.json deleted file mode 100644 index 0488262b..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tn.imageset/tn@2x.png b/Passepartout/App/Flags.xcassets/flags/tn.imageset/tn@2x.png deleted file mode 100644 index f1a09a7c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tn.imageset/tn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tn.imageset/tn@3x.png b/Passepartout/App/Flags.xcassets/flags/tn.imageset/tn@3x.png deleted file mode 100644 index b88f5c35..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tn.imageset/tn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/to.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/to.imageset/Contents.json deleted file mode 100644 index f63e7a15..00000000 --- a/Passepartout/App/Flags.xcassets/flags/to.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "to@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "to@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/to.imageset/to@2x.png b/Passepartout/App/Flags.xcassets/flags/to.imageset/to@2x.png deleted file mode 100644 index ade51402..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/to.imageset/to@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/to.imageset/to@3x.png b/Passepartout/App/Flags.xcassets/flags/to.imageset/to@3x.png deleted file mode 100644 index 94ee6b28..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/to.imageset/to@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tr.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tr.imageset/Contents.json deleted file mode 100644 index a18ec00f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tr.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tr@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tr@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tr.imageset/tr@2x.png b/Passepartout/App/Flags.xcassets/flags/tr.imageset/tr@2x.png deleted file mode 100644 index c28d96fa..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tr.imageset/tr@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tr.imageset/tr@3x.png b/Passepartout/App/Flags.xcassets/flags/tr.imageset/tr@3x.png deleted file mode 100644 index 587ca607..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tr.imageset/tr@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tt.imageset/Contents.json deleted file mode 100644 index 39c55a5a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tt.imageset/tt@2x.png b/Passepartout/App/Flags.xcassets/flags/tt.imageset/tt@2x.png deleted file mode 100644 index 9312127c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tt.imageset/tt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tt.imageset/tt@3x.png b/Passepartout/App/Flags.xcassets/flags/tt.imageset/tt@3x.png deleted file mode 100644 index 33c68047..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tt.imageset/tt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tv.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tv.imageset/Contents.json deleted file mode 100644 index a25b8ffe..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tv.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tv@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tv@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tv.imageset/tv@2x.png b/Passepartout/App/Flags.xcassets/flags/tv.imageset/tv@2x.png deleted file mode 100644 index bc0ef263..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tv.imageset/tv@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tv.imageset/tv@3x.png b/Passepartout/App/Flags.xcassets/flags/tv.imageset/tv@3x.png deleted file mode 100644 index e054a8c2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tv.imageset/tv@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tw.imageset/Contents.json deleted file mode 100644 index 6cbb7236..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tw.imageset/tw@2x.png b/Passepartout/App/Flags.xcassets/flags/tw.imageset/tw@2x.png deleted file mode 100644 index d2b66751..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tw.imageset/tw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tw.imageset/tw@3x.png b/Passepartout/App/Flags.xcassets/flags/tw.imageset/tw@3x.png deleted file mode 100644 index 6d00342a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tw.imageset/tw@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/tz.imageset/Contents.json deleted file mode 100644 index d903bbe9..00000000 --- a/Passepartout/App/Flags.xcassets/flags/tz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/tz.imageset/tz@2x.png b/Passepartout/App/Flags.xcassets/flags/tz.imageset/tz@2x.png deleted file mode 100644 index 587a5d13..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tz.imageset/tz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/tz.imageset/tz@3x.png b/Passepartout/App/Flags.xcassets/flags/tz.imageset/tz@3x.png deleted file mode 100644 index dcefe3b6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/tz.imageset/tz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ua.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ua.imageset/Contents.json deleted file mode 100644 index 4ae29332..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ua.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ua@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ua@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ua.imageset/ua@2x.png b/Passepartout/App/Flags.xcassets/flags/ua.imageset/ua@2x.png deleted file mode 100644 index 3dc53400..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ua.imageset/ua@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ua.imageset/ua@3x.png b/Passepartout/App/Flags.xcassets/flags/ua.imageset/ua@3x.png deleted file mode 100644 index 2855bc8c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ua.imageset/ua@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ug.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ug.imageset/Contents.json deleted file mode 100644 index 37e3a88d..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ug.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ug@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ug@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ug.imageset/ug@2x.png b/Passepartout/App/Flags.xcassets/flags/ug.imageset/ug@2x.png deleted file mode 100644 index 68bed5ea..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ug.imageset/ug@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ug.imageset/ug@3x.png b/Passepartout/App/Flags.xcassets/flags/ug.imageset/ug@3x.png deleted file mode 100644 index bbe3a42d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ug.imageset/ug@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/um.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/um.imageset/Contents.json deleted file mode 100644 index 8771cd57..00000000 --- a/Passepartout/App/Flags.xcassets/flags/um.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "um@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "um@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/um.imageset/um@2x.png b/Passepartout/App/Flags.xcassets/flags/um.imageset/um@2x.png deleted file mode 100644 index 033380f4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/um.imageset/um@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/um.imageset/um@3x.png b/Passepartout/App/Flags.xcassets/flags/um.imageset/um@3x.png deleted file mode 100644 index 36867161..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/um.imageset/um@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/un.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/un.imageset/Contents.json deleted file mode 100644 index 39ed99a8..00000000 --- a/Passepartout/App/Flags.xcassets/flags/un.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "un@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "un@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/un.imageset/un@2x.png b/Passepartout/App/Flags.xcassets/flags/un.imageset/un@2x.png deleted file mode 100644 index 375886c2..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/un.imageset/un@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/un.imageset/un@3x.png b/Passepartout/App/Flags.xcassets/flags/un.imageset/un@3x.png deleted file mode 100644 index fe15ca9c..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/un.imageset/un@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/us.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/us.imageset/Contents.json deleted file mode 100644 index 94c9f941..00000000 --- a/Passepartout/App/Flags.xcassets/flags/us.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "us@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "us@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/us.imageset/us@2x.png b/Passepartout/App/Flags.xcassets/flags/us.imageset/us@2x.png deleted file mode 100644 index 033380f4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/us.imageset/us@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/us.imageset/us@3x.png b/Passepartout/App/Flags.xcassets/flags/us.imageset/us@3x.png deleted file mode 100644 index 36867161..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/us.imageset/us@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/uy.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/uy.imageset/Contents.json deleted file mode 100644 index 126bbc73..00000000 --- a/Passepartout/App/Flags.xcassets/flags/uy.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "uy@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "uy@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/uy.imageset/uy@2x.png b/Passepartout/App/Flags.xcassets/flags/uy.imageset/uy@2x.png deleted file mode 100644 index 53b2212d..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/uy.imageset/uy@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/uy.imageset/uy@3x.png b/Passepartout/App/Flags.xcassets/flags/uy.imageset/uy@3x.png deleted file mode 100644 index da6f83e7..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/uy.imageset/uy@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/uz.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/uz.imageset/Contents.json deleted file mode 100644 index 1c68372c..00000000 --- a/Passepartout/App/Flags.xcassets/flags/uz.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "uz@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "uz@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/uz.imageset/uz@2x.png b/Passepartout/App/Flags.xcassets/flags/uz.imageset/uz@2x.png deleted file mode 100644 index 5603240a..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/uz.imageset/uz@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/uz.imageset/uz@3x.png b/Passepartout/App/Flags.xcassets/flags/uz.imageset/uz@3x.png deleted file mode 100644 index c2cc61e3..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/uz.imageset/uz@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/va.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/va.imageset/Contents.json deleted file mode 100644 index 017f6979..00000000 --- a/Passepartout/App/Flags.xcassets/flags/va.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "va@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "va@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/va.imageset/va@2x.png b/Passepartout/App/Flags.xcassets/flags/va.imageset/va@2x.png deleted file mode 100644 index c7f3e900..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/va.imageset/va@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/va.imageset/va@3x.png b/Passepartout/App/Flags.xcassets/flags/va.imageset/va@3x.png deleted file mode 100644 index b1837a5b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/va.imageset/va@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vc.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/vc.imageset/Contents.json deleted file mode 100644 index 1240cdde..00000000 --- a/Passepartout/App/Flags.xcassets/flags/vc.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "vc@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "vc@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/vc.imageset/vc@2x.png b/Passepartout/App/Flags.xcassets/flags/vc.imageset/vc@2x.png deleted file mode 100644 index 71a502e8..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vc.imageset/vc@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vc.imageset/vc@3x.png b/Passepartout/App/Flags.xcassets/flags/vc.imageset/vc@3x.png deleted file mode 100644 index 6853ddc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vc.imageset/vc@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ve.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ve.imageset/Contents.json deleted file mode 100644 index 1ccf5bc3..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ve.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ve@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ve@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ve.imageset/ve@2x.png b/Passepartout/App/Flags.xcassets/flags/ve.imageset/ve@2x.png deleted file mode 100644 index 382ffe3f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ve.imageset/ve@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ve.imageset/ve@3x.png b/Passepartout/App/Flags.xcassets/flags/ve.imageset/ve@3x.png deleted file mode 100644 index 09ac0b78..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ve.imageset/ve@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vg.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/vg.imageset/Contents.json deleted file mode 100644 index 257af5ff..00000000 --- a/Passepartout/App/Flags.xcassets/flags/vg.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "vg@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "vg@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/vg.imageset/vg@2x.png b/Passepartout/App/Flags.xcassets/flags/vg.imageset/vg@2x.png deleted file mode 100644 index 6f41196e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vg.imageset/vg@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vg.imageset/vg@3x.png b/Passepartout/App/Flags.xcassets/flags/vg.imageset/vg@3x.png deleted file mode 100644 index 7c99c5da..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vg.imageset/vg@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vi.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/vi.imageset/Contents.json deleted file mode 100644 index 8c5e4610..00000000 --- a/Passepartout/App/Flags.xcassets/flags/vi.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "vi@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "vi@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/vi.imageset/vi@2x.png b/Passepartout/App/Flags.xcassets/flags/vi.imageset/vi@2x.png deleted file mode 100644 index 2bbf40ac..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vi.imageset/vi@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vi.imageset/vi@3x.png b/Passepartout/App/Flags.xcassets/flags/vi.imageset/vi@3x.png deleted file mode 100644 index fe62dfcd..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vi.imageset/vi@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vn.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/vn.imageset/Contents.json deleted file mode 100644 index 20975668..00000000 --- a/Passepartout/App/Flags.xcassets/flags/vn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "vn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "vn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/vn.imageset/vn@2x.png b/Passepartout/App/Flags.xcassets/flags/vn.imageset/vn@2x.png deleted file mode 100644 index ab509368..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vn.imageset/vn@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vn.imageset/vn@3x.png b/Passepartout/App/Flags.xcassets/flags/vn.imageset/vn@3x.png deleted file mode 100644 index 17ca2e02..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vn.imageset/vn@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vu.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/vu.imageset/Contents.json deleted file mode 100644 index 4effe554..00000000 --- a/Passepartout/App/Flags.xcassets/flags/vu.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "vu@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "vu@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/vu.imageset/vu@2x.png b/Passepartout/App/Flags.xcassets/flags/vu.imageset/vu@2x.png deleted file mode 100644 index 42b739bb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vu.imageset/vu@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/vu.imageset/vu@3x.png b/Passepartout/App/Flags.xcassets/flags/vu.imageset/vu@3x.png deleted file mode 100644 index a01fa93b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/vu.imageset/vu@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/wf.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/wf.imageset/Contents.json deleted file mode 100644 index 11c0d01a..00000000 --- a/Passepartout/App/Flags.xcassets/flags/wf.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "wf@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "wf@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/wf.imageset/wf@2x.png b/Passepartout/App/Flags.xcassets/flags/wf.imageset/wf@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/wf.imageset/wf@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/wf.imageset/wf@3x.png b/Passepartout/App/Flags.xcassets/flags/wf.imageset/wf@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/wf.imageset/wf@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ws.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ws.imageset/Contents.json deleted file mode 100644 index b27b0e25..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ws.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ws@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ws@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ws.imageset/ws@2x.png b/Passepartout/App/Flags.xcassets/flags/ws.imageset/ws@2x.png deleted file mode 100644 index 3f82e9bb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ws.imageset/ws@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ws.imageset/ws@3x.png b/Passepartout/App/Flags.xcassets/flags/ws.imageset/ws@3x.png deleted file mode 100644 index f345473f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ws.imageset/ws@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/xk.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/xk.imageset/Contents.json deleted file mode 100644 index 9cc7c031..00000000 --- a/Passepartout/App/Flags.xcassets/flags/xk.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "xk@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "xk@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/xk.imageset/xk@2x.png b/Passepartout/App/Flags.xcassets/flags/xk.imageset/xk@2x.png deleted file mode 100644 index 7e43a438..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/xk.imageset/xk@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/xk.imageset/xk@3x.png b/Passepartout/App/Flags.xcassets/flags/xk.imageset/xk@3x.png deleted file mode 100644 index 0c3c233b..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/xk.imageset/xk@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ye.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/ye.imageset/Contents.json deleted file mode 100644 index 682860a1..00000000 --- a/Passepartout/App/Flags.xcassets/flags/ye.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ye@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "ye@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/ye.imageset/ye@2x.png b/Passepartout/App/Flags.xcassets/flags/ye.imageset/ye@2x.png deleted file mode 100644 index 9397d9bb..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ye.imageset/ye@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/ye.imageset/ye@3x.png b/Passepartout/App/Flags.xcassets/flags/ye.imageset/ye@3x.png deleted file mode 100644 index 3ac166ab..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/ye.imageset/ye@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/yt.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/yt.imageset/Contents.json deleted file mode 100644 index ce85b9dd..00000000 --- a/Passepartout/App/Flags.xcassets/flags/yt.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "yt@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "yt@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/yt.imageset/yt@2x.png b/Passepartout/App/Flags.xcassets/flags/yt.imageset/yt@2x.png deleted file mode 100644 index ca9befc4..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/yt.imageset/yt@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/yt.imageset/yt@3x.png b/Passepartout/App/Flags.xcassets/flags/yt.imageset/yt@3x.png deleted file mode 100644 index 397d305f..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/yt.imageset/yt@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/za.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/za.imageset/Contents.json deleted file mode 100644 index f00ea47f..00000000 --- a/Passepartout/App/Flags.xcassets/flags/za.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "za@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "za@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/za.imageset/za@2x.png b/Passepartout/App/Flags.xcassets/flags/za.imageset/za@2x.png deleted file mode 100644 index eceec411..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/za.imageset/za@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/za.imageset/za@3x.png b/Passepartout/App/Flags.xcassets/flags/za.imageset/za@3x.png deleted file mode 100644 index d85b1024..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/za.imageset/za@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/zm.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/zm.imageset/Contents.json deleted file mode 100644 index 5def39bf..00000000 --- a/Passepartout/App/Flags.xcassets/flags/zm.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "zm@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "zm@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/zm.imageset/zm@2x.png b/Passepartout/App/Flags.xcassets/flags/zm.imageset/zm@2x.png deleted file mode 100644 index 30c36e65..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/zm.imageset/zm@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/zm.imageset/zm@3x.png b/Passepartout/App/Flags.xcassets/flags/zm.imageset/zm@3x.png deleted file mode 100644 index 3a0c639e..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/zm.imageset/zm@3x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/zw.imageset/Contents.json b/Passepartout/App/Flags.xcassets/flags/zw.imageset/Contents.json deleted file mode 100644 index 40b25c00..00000000 --- a/Passepartout/App/Flags.xcassets/flags/zw.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "zw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "zw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Flags.xcassets/flags/zw.imageset/zw@2x.png b/Passepartout/App/Flags.xcassets/flags/zw.imageset/zw@2x.png deleted file mode 100644 index 27170c02..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/zw.imageset/zw@2x.png and /dev/null differ diff --git a/Passepartout/App/Flags.xcassets/flags/zw.imageset/zw@3x.png b/Passepartout/App/Flags.xcassets/flags/zw.imageset/zw@3x.png deleted file mode 100644 index 8cc678f6..00000000 Binary files a/Passepartout/App/Flags.xcassets/flags/zw.imageset/zw@3x.png and /dev/null differ diff --git a/Passepartout/App/Info.plist b/Passepartout/App/Info.plist deleted file mode 100644 index b92f4e88..00000000 --- a/Passepartout/App/Info.plist +++ /dev/null @@ -1,110 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Passepartout - CFBundleDocumentTypes - - - CFBundleTypeName - All files - CFBundleTypeRole - Viewer - LSHandlerRank - Default - LSItemContentTypes - - public.item - - - - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 2.3.6 - CFBundleVersion - 3630 - ITSAppUsesNonExemptEncryption - - LSApplicationCategoryType - public.app-category.utilities - LSApplicationQueriesSchemes - - mailto - twitch - - LSRequiresIPhoneOS - - NSFaceIDUsageDescription - dummy - NSHumanReadableCopyright - $(CFG_COPYRIGHT) - NSLocationWhenInUseUsageDescription - dummy - NSUserActivityTypes - - ConnectVPNIntent - DisableVPNIntent - EnableVPNIntent - MoveToLocationIntent - TrustCellularNetworkIntent - TrustCurrentNetworkIntent - UntrustCellularNetworkIntent - UntrustCurrentNetworkIntent - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - - UIBackgroundModes - - remote-notification - - UIFileSharingEnabled - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportsDocumentBrowser - - com.algoritmico.Passepartout.config - - appstore_id - $(CFG_APPSTORE_ID) - cloudkit_id - iCloud.$(CFG_GROUP_ID) - cloudkit_shared_id - iCloud.$(CFG_GROUP_ID).Shared - group_id - group.$(CFG_GROUP_ID) - - - diff --git a/Passepartout/App/L10n/Core+L10n.swift b/Passepartout/App/L10n/Core+L10n.swift deleted file mode 100644 index 7457243f..00000000 --- a/Passepartout/App/L10n/Core+L10n.swift +++ /dev/null @@ -1,194 +0,0 @@ -// -// Core+L10n.swift -// Passepartout -// -// Created by Davide De Rosa on 2/26/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -extension ObservableVPNState: StyledLocalizableEntity { - public enum Style { - case status(isActiveProfile: Bool, withErrors: Bool, dataCountIfAvailable: Bool) - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .status(let isActiveProfile, let withErrors, let dataCountIfAvailable): - return statusDescription(isActiveProfile: isActiveProfile, withErrors: withErrors, dataCountIfAvailable: dataCountIfAvailable) - } - } - - private func statusDescription(isActiveProfile: Bool, withErrors: Bool, dataCountIfAvailable: Bool) -> String { - guard isActiveProfile && isEnabled else { - return L10n.Tunnelkit.Vpn.disabled - } - if withErrors, let lastError { - return AppError(lastError).localizedDescription - } - if dataCountIfAvailable, vpnStatus == .connected, let dataCount = dataCount { - return dataCount.localizedDescription - } - return vpnStatus.localizedDescription - } -} - -extension Profile: Comparable { - public static func < (lhs: Self, rhs: Self) -> Bool { - lhs.header < rhs.header - } -} - -extension Profile.Header: Comparable { - public static func < (lhs: Self, rhs: Self) -> Bool { - lhs.name.lowercased() < rhs.name.lowercased() - } -} - -extension Profile.OpenVPNSettings: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case endpoint - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .endpoint: - return endpointDescription - } - } - - private var endpointDescription: String? { - customEndpoint?.address ?? configuration.remotes?.first?.address - } -} - -extension Profile.WireGuardSettings: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case endpoint - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .endpoint: - return endpointDescription - } - } - - private var endpointDescription: String? { - configuration.tunnelConfiguration.peers.first?.endpoint?.stringRepresentation - } -} - -extension Profile.OnDemand.Policy: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .any: - return L10n.OnDemand.Policy.any - - case .including: - return L10n.OnDemand.Policy.including - - case .excluding: - return L10n.OnDemand.Policy.excluding - } - } -} - -extension Network.Choice: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .automatic: - return L10n.Global.Strings.automatic - - case .manual: - return L10n.Global.Strings.manual - } - } -} - -extension Network.DNSSettings.ConfigurationType: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .plain: - return Unlocalized.DNS.plain - - case .https: - return Unlocalized.Network.https - - case .tls: - return Unlocalized.Network.tls - - case .disabled: - return L10n.Global.Strings.disabled - } - } -} - -extension Network.ProxySettings.ConfigurationType: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .manual: - return L10n.Global.Strings.manual - - case .pac: - return Unlocalized.Network.proxyAutoConfiguration - - case .disabled: - return L10n.Global.Strings.disabled - } - } -} - -extension Profile.Account.AuthenticationMethod: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .persistent: - return L10n.Account.Items.AuthenticationMethod.persistent - - case .interactive: - return L10n.Account.Items.AuthenticationMethod.interactive - - case .totp: - return Unlocalized.Other.totp - } - } -} - -extension Int: StyledLocalizableEntity { - public enum Style { - case mtu - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .mtu: - return mtuDescription - } - } - - private var mtuDescription: String { - guard self != 0 else { - return L10n.Global.Strings.default - } - return description - } -} diff --git a/Passepartout/App/L10n/Errors+L10n.swift b/Passepartout/App/L10n/Errors+L10n.swift deleted file mode 100644 index 5981539e..00000000 --- a/Passepartout/App/L10n/Errors+L10n.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// Errors+L10n.swift -// Passepartout -// -// Created by Davide De Rosa on 5/30/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -extension AppError: LocalizedError { - var errorDescription: String? { - guard let errorDescriptionImpl, !errorDescriptionImpl.isEmpty else { - return localizedDescription - } - return errorDescriptionImpl - } - - private var errorDescriptionImpl: String? { - let V = L10n.Global.Errors.self - switch self { - case .profile(let domainError): - switch domainError { - case .importFailure(let error): - return error.localizedDescription - - case .decryptionFailure(let error): - return error.localizedDescription - - case .notFound: - return V.missingProfile - - case .failedToFetchProvider(_, let error): - return error.localizedDescription - } - - case .provider(let domainError): - switch domainError { - case .fetchFailure(let error): - return error.localizedDescription - } - - case .vpn(let domainError): - switch domainError { - case .notProvider: - assertionFailure() - return nil - - case .providerServerNotFound: - return V.missingProviderServer - - case .providerPresetNotFound: - return V.missingProviderPreset - - case .missingAccount: - return V.missingAccount - - case .emptyEndpoints: - assertionFailure() - return nil - } - - case .tunnel(let tunnelError): - switch tunnelError { - case .expired: - return V.tunnelExpired - } - - case .generic(let error): - return error.localizedDescription - } - } -} - -extension String { - var withTrailingDot: String { - guard !hasSuffix(".") else { - return self - } - return "\(self)." - } -} diff --git a/Passepartout/App/L10n/OpenVPN+L10n.swift b/Passepartout/App/L10n/OpenVPN+L10n.swift deleted file mode 100644 index ceb8817e..00000000 --- a/Passepartout/App/L10n/OpenVPN+L10n.swift +++ /dev/null @@ -1,312 +0,0 @@ -// -// OpenVPN+L10n.swift -// Passepartout -// -// Created by Davide De Rosa on 2/26/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary -import TunnelKitOpenVPN - -extension OpenVPN.Cipher: LocalizableEntity { - public var localizedDescription: String { - description - } -} - -extension OpenVPN.Digest: LocalizableEntity { - public var localizedDescription: String { - description - } -} - -extension OpenVPN.CompressionFraming: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .disabled: - return L10n.Global.Strings.disabled - - case .compLZO: - return Unlocalized.OpenVPN.compLZO - - case .compress, .compressV2: - return Unlocalized.OpenVPN.compress - } - } -} - -extension OpenVPN.CompressionAlgorithm: LocalizableEntity { - public var localizedDescription: String { - let V = L10n.Endpoint.Advanced.Openvpn.Items.self - switch self { - case .disabled: - return L10n.Global.Strings.disabled - - case .LZO: - return Unlocalized.OpenVPN.lzo - - case .other: - return V.CompressionAlgorithm.Value.other - } - } -} - -extension OpenVPN.XORMethod: StyledLocalizableEntity { - public enum Style { - case short - - case long - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .short: - return shortDescription - - case .long: - return longDescription - } - } - - private var shortDescription: String { - switch self { - case .xormask: - return Unlocalized.OpenVPN.XOR.xormask.rawValue - - case .xorptrpos: - return Unlocalized.OpenVPN.XOR.xorptrpos.rawValue - - case .reverse: - return Unlocalized.OpenVPN.XOR.reverse.rawValue - - case .obfuscate: - return Unlocalized.OpenVPN.XOR.obfuscate.rawValue - } - } - - private var longDescription: String { - switch self { - case .xormask(let mask): - return "\(shortDescription) \(mask.toHex())" - - case .obfuscate(let mask): - return "\(shortDescription) \(mask.toHex())" - - default: - return shortDescription - } - } -} - -extension OpenVPN.PullMask: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .routes: - return L10n.Endpoint.Advanced.Openvpn.Items.Route.caption - - case .dns: - return Unlocalized.Network.dns - - case .proxy: - return L10n.Global.Strings.proxy - } - } -} - -extension OpenVPN.ConfigurationBuilder: StyledLocalizableEntity { - public enum Style { - case tlsWrap - - case eku - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .tlsWrap: - return tlsWrap.tlsWrapDescription - - case .eku: - return checksEKU.ekuDescription - } - } -} - -extension OpenVPN.Configuration: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case keepAlive - - case renegotiatesAfter - - case randomizeEndpoint - - case randomizeHostnames - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .keepAlive: - return keepAliveInterval?.keepAliveDescription - - case .renegotiatesAfter: - return renegotiatesAfter?.renegotiatesAfterDescription - - case .randomizeEndpoint: - return randomizeEndpoint?.randomizeEndpointDescription - - case .randomizeHostnames: - return randomizeHostnames?.randomizeHostnamesDescription - } - } -} - -private extension Optional where Wrapped == OpenVPN.TLSWrap { - var tlsWrapDescription: String { - guard let strategy = self?.strategy else { - return L10n.Global.Strings.disabled - } - let V = L10n.Endpoint.Advanced.Openvpn.Items.self - switch strategy { - case .auth: - return V.TlsWrapping.Value.auth - - case .crypt: - return V.TlsWrapping.Value.crypt - } - } -} - -private extension TimeInterval { - var keepAliveDescription: String { - let V = L10n.Endpoint.Advanced.Openvpn.Items.self - if self > 0 { - return V.KeepAlive.Value.seconds(Int(self)) - } else { - return L10n.Global.Strings.disabled - } - } -} - -private extension Optional where Wrapped == Bool { - var ekuDescription: String { - let V = L10n.Global.Strings.self - return (self ?? false) ? V.enabled : V.disabled - } -} - -private extension TimeInterval { - var renegotiatesAfterDescription: String { - let V = L10n.Endpoint.Advanced.Openvpn.Items.self - if self > 0 { - return V.RenegotiationSeconds.Value.after(TimeInterval(self).localizedDescription) - } else { - return L10n.Global.Strings.disabled - } - } -} - -private extension Bool { - var randomizeEndpointDescription: String { - let V = L10n.Global.Strings.self - return self ? V.enabled : V.disabled - } - - var randomizeHostnamesDescription: String { - let V = L10n.Global.Strings.self - return self ? V.enabled : V.disabled - } -} - -// MARK: - Errors - -extension TunnelKitOpenVPNError: LocalizedError { - public var errorDescription: String? { - let V = L10n.Tunnelkit.Errors.Vpn.self - switch self { - case .socketActivity, .timeout: - return V.timeout - - case .dnsFailure: - return V.dns - - case .tlsInitialization, .tlsServerVerification, .tlsHandshake: - return V.tls - - case .authentication: - return V.auth - - case .encryptionInitialization, .encryptionData: - return V.encryption - - case .serverCompression, .lzo: - return V.compression - - case .networkChanged: - return V.network - - case .routing: - return V.routing - - case .gatewayUnattainable: - return V.gateway - - case .serverShutdown: - return V.shutdown - - default: - return L10n.Global.Strings.unknown - } - } -} - -extension OpenVPN.ConfigurationError: LocalizedError { - public var errorDescription: String? { - let V = L10n.Tunnelkit.Errors.Openvpn.self - switch self { - case .encryptionPassphrase: - pp_log.error("Could not parse configuration URL: unable to decrypt, passphrase required") - return V.passphraseRequired - - case .unableToDecrypt(let error): - pp_log.error("Could not parse configuration URL: unable to decrypt, \(error.localizedDescription)") - return V.decryption - - case .malformed(let option): - pp_log.error("Could not parse configuration URL: malformed option, \(option)") - return V.malformed(option) - - case .missingConfiguration(let option): - pp_log.error("Could not parse configuration URL: missing configuration, \(option)") - return V.requiredOption(option) - - case .unsupportedConfiguration(var option): - if option.contains("external") { - option.append(" (see FAQ)") - } - pp_log.error("Could not parse configuration URL: unsupported configuration, \(option)") - return V.unsupportedOption(option) - - case .continuationPushReply: - assertionFailure("This is a server-side configuration parsing error") - return L10n.Global.Strings.unknown - } - } -} diff --git a/Passepartout/App/L10n/Providers+L10n.swift b/Passepartout/App/L10n/Providers+L10n.swift deleted file mode 100644 index c76ff4a2..00000000 --- a/Passepartout/App/L10n/Providers+L10n.swift +++ /dev/null @@ -1,198 +0,0 @@ -// -// Providers+L10n.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -extension ProviderManager: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case preset(profile: Profile) - - case infrastructureUpdate(profile: Profile) - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .preset(let profile): - return presetDescription(forProfile: profile) - - case .infrastructureUpdate(let profile): - return infrastructureUpdateDescription(forProfile: profile) - } - } - - private func presetDescription(forProfile profile: Profile) -> String? { - guard let server = profile.providerServer(self) else { - return nil - } - return profile.providerPreset(server)?.localizedDescription - } - - private func infrastructureUpdateDescription(forProfile profile: Profile) -> String? { - guard let providerName = profile.header.providerName else { - return nil - } - return lastUpdate(providerName, vpnProtocol: profile.currentVPNProtocol)?.timestamp - } -} - -extension ProviderMetadata: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case guidance - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .guidance: - return guidanceString - } - } - - private var guidanceString: String? { - let prefix = "account.sections.guidance.footer.infrastructure" - let key = "\(prefix).\(name)" - var format = NSLocalizedString(key, bundle: .main, comment: "") - - // i.e. key not found - if format == key { - let purpose = name.credentialsPurpose - let defaultKey = "\(prefix).default.\(purpose)" - format = NSLocalizedString(defaultKey, bundle: .main, comment: "") - } - - return String(format: format, locale: .current, fullName) - } -} - -extension ProviderLocation: StyledLocalizableEntity { - public enum Style { - case country - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .country: - return countryDescription - } - } - - private var countryDescription: String { - countryCode.localizedAsCountryCode - } -} - -extension ProviderServer: StyledLocalizableEntity { - public enum Style { - case country - - case countryWithCategory(withCategory: Bool) - - case shortWithDefault - - case longWithCategory(withCategory: Bool) - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .country: - return countryDescription - - case .countryWithCategory(let withCategory): - return countryDescription(withCategory: withCategory) - - case .shortWithDefault: - return shortDescriptionWithDefault - - case .longWithCategory(let withCategory): - return longDescription(withCategory: withCategory) - } - } - - private var countryDescription: String { - countryCode.localizedAsCountryCode - } - - private func countryDescription(withCategory: Bool) -> String { - let desc = countryDescription - if withCategory, !categoryName.isEmpty { - return "\(categoryName.uppercased()): \(desc)" - } - return desc - } - - private var shortDescriptionWithDefault: String { - shortDescription ?? "\(L10n.Global.Strings.default) [\(apiId)]" - } - - private func longDescription(withCategory: Bool) -> String { - var comps: [String] = [countryDescription] - shortDescription.map { - comps.append($0) - } - let desc = comps.joined(separator: ", ") - if withCategory, !categoryName.isEmpty { - return "\(categoryName.uppercased()): \(desc)" - } - return desc - } -} - -extension ProviderServer: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case short - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .short: - return shortDescription - } - } - - private var shortDescription: String? { - var comps = localizedName.map { [$0] } ?? [] - if let serverIndex = serverIndex { - comps.append("#\(serverIndex)") - } - guard !comps.isEmpty else { - return nil - } - var str = comps.joined(separator: " ") - if let tags = tags { - let suffix = tags.map { $0.uppercased() }.joined(separator: ",") - str = "\(str) (\(suffix))" - } - guard !str.isEmpty else { - return nil - } - return str - } -} - -extension ProviderServer.Preset: LocalizableEntity { - public var localizedDescription: String { - name - } -} diff --git a/Passepartout/App/L10n/TunnelKit+L10n.swift b/Passepartout/App/L10n/TunnelKit+L10n.swift deleted file mode 100644 index a6adf9c9..00000000 --- a/Passepartout/App/L10n/TunnelKit+L10n.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// TunnelKit+L10n.swift -// Passepartout -// -// Created by Davide De Rosa on 3/12/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import NetworkExtension -import PassepartoutLibrary -import TunnelKitManager -import TunnelKitOpenVPN -import TunnelKitWireGuard - -extension VPNStatus: LocalizableEntity { - public var localizedDescription: String { - switch self { - case .connecting: - return L10n.Tunnelkit.Vpn.connecting - - case .connected: - return L10n.Tunnelkit.Vpn.active - - case .disconnecting: - return L10n.Tunnelkit.Vpn.disconnecting - - case .disconnected: - return L10n.Tunnelkit.Vpn.inactive - } - } -} - -extension DataCount: LocalizableEntity { - public var localizedDescription: String { - let down = received.descriptionAsDataUnit - let up = sent.descriptionAsDataUnit - return "↓\(down) ↑\(up)" - } -} - -extension IPv4Settings: StyledLocalizableEntity { - public enum Style { - case address - - case defaultGateway - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .address: - return addressDescription - - case .defaultGateway: - return defaultGatewayDescription - } - } - - private var addressDescription: String { - "\(address)/\(addressMask)" - } - - private var defaultGatewayDescription: String { - defaultGateway - } -} - -extension IPv6Settings: StyledLocalizableEntity { - public enum Style { - case address - - case defaultGateway - } - - public func localizedDescription(style: Style) -> String { - switch style { - case .address: - return addressDescription - - case .defaultGateway: - return defaultGatewayDescription - } - } - - private var addressDescription: String { - "\(address)/\(addressPrefixLength)" - } - - private var defaultGatewayDescription: String { - defaultGateway - } -} - -extension IPv4Settings.Route: LocalizableEntity { - public var localizedDescription: String { - "\(destination)/\(mask) → \(gateway ?? "*")" - } -} - -extension IPv6Settings.Route: LocalizableEntity { - public var localizedDescription: String { - "\(destination)/\(prefixLength) → \(gateway ?? "*")" - } -} diff --git a/Passepartout/App/L10n/Unlocalized.swift b/Passepartout/App/L10n/Unlocalized.swift deleted file mode 100644 index 3cc6ed5b..00000000 --- a/Passepartout/App/L10n/Unlocalized.swift +++ /dev/null @@ -1,299 +0,0 @@ -// -// Unlocalized.swift -// Passepartout -// -// Created by Davide De Rosa on 2/26/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -enum Unlocalized { - static let appName = Constants.Global.appName - - enum Placeholders { - static let empty = "" - - static let address = "0.0.0.0" - - static let port = "8080" - - static let hostname = "example.com" - - static let dohURL = "https://example.com/dns-query" - - static let dotServerName = hostname - - static let dnsAddress = address - - static let dnsDomain = hostname - - static let pacURL = "https://proxy/auto-conf" - - static let proxyBypassDomain = hostname - } - - enum DNS { - static let plain = "Cleartext" - } - - enum Keychain { - static func passwordEntry(_ profile: Profile) -> String { - [ - profile.id.uuidString, - profile.currentVPNProtocol.keychainEntry, - profile.account.username - ].joined(separator: ":") - } - - static func passwordLabel(_ profile: Profile) -> String { - "\(Constants.Global.appName): \(profile.header.name) (\(profile.currentVPNProtocol.keychainEntry))" - } - } - - enum Issues { - static let recipient = "issues@\(Constants.Domain.name)" - - static let subject = "\(appName) - Report issue" - - static let bodySentinel = "" - - static let maxLogBytes = UInt64(20000) - - enum Filenames { - static let mime = "text/plain" - - static var appLog: String { - let fmt = DateFormatter() - fmt.dateFormat = "yyyyMMdd-HHmmss" - let iso = fmt.string(from: Date()) - return "app-\(iso).txt" - } - - static var tunnelLog: String { - let fmt = DateFormatter() - fmt.dateFormat = "yyyyMMdd-HHmmss" - let iso = fmt.string(from: Date()) - return "tunnel-\(iso).txt" - } - } - - private static func rawBody(_ description: String, _ metadata: String) -> String { - "Hi,\n\n\(description)\n\n\(metadata)\n\nRegards" - } - - static func body( - providerMetadata: ProviderMetadata?, - lastUpdate: Date?, - purchasedProductIdentifiers: Set? - ) -> String { - var content: [String] = ["Hi,\n"] - content.append(bodySentinel) - content.append("\n--\n") - content.append(DebugLog.decoratedMetadataString()) - if let providerMetadata { - if let lastUpdate { - content.append("Provider: \(providerMetadata.fullName) (last updated: \(lastUpdate))") - } else { - content.append("Provider: \(providerMetadata.fullName)") - } - } - if let purchasedProductIdentifiers { - content.append("Purchased: \(purchasedProductIdentifiers)") - } - content.append("\n--\n") - content.append("Regards") - return content.joined(separator: "\n") - } - } - - enum Social { - static let reddit = "Reddit" - - private static let twitterHashtags = ["OpenVPN", "WireGuard", "iOS", "macOS"] - - static func twitterIntent(withMessage message: String) -> URL { - var text = message - for ht in twitterHashtags { - text = text.replacingOccurrences(of: ht, with: "#\(ht)") - } - var comps = URLComponents(string: "https://twitter.com/intent/tweet")! - comps.queryItems = [ - URLQueryItem(name: "url", value: Constants.URLs.website.absoluteString), - URLQueryItem(name: "via", value: "keeshux"), - URLQueryItem(name: "text", value: text) - ] - return comps.url! - } - } - - enum Translations { - enum Email { - static let recipient = "translate@\(Constants.Domain.name)" - - static let subject = "\(appName) - Translations" - - static func body(_ description: String) -> String { - "Hi,\n\n\(description)\n\nRegards" - } - - static let template = "I offer to translate to: " - } - - static let translators: [String: String] = [ - "de": "Christian Lederer, Theodor Tietze", - "el": "Konstantinos Koukoulakis", - "en-US": "Davide De Rosa", - "es": "Davide De Rosa, Elena Vivó", - "fr-FR": "Julien Laniel", - "it": "Davide De Rosa", - "nl": "Norbert de Vreede", - "pl": "Piotr Książek", - "pt-BR": "Helder Santana", - "ru": "Alexander Korobynikov", - "sv": "Henry Gross-Hellsen", - "ua": "Dmitry Chirkin", - "zh-Hans": "OnlyThen" - ] - } - - enum Credits { - static let author = "Davide De Rosa" - - static let licenses: [GenericCreditsView.License] = [.init( - "Kvitto", - "BSD", - URL(string: "https://raw.githubusercontent.com/Cocoanetics/Kvitto/develop/LICENSE")! - ), .init( - "lzo", - "GPLv2", - URL(string: "https://www.gnu.org/licenses/gpl-2.0.txt")! - ), .init( - "OpenSSL", - "OpenSSL", - URL(string: "https://raw.githubusercontent.com/openssl/openssl/master/LICENSE.txt")! - ), .init( - "PIATunnel", - "MIT", - URL(string: "https://raw.githubusercontent.com/pia-foss/tunnel-apple/master/LICENSE")! - ), .init( - "SwiftGen", - "MIT", - URL(string: "https://raw.githubusercontent.com/SwiftGen/SwiftGen/master/LICENCE")! - ), .init( - "SwiftyBeaver", - "MIT", - URL(string: "https://raw.githubusercontent.com/SwiftyBeaver/SwiftyBeaver/master/LICENSE")! - )] - - static let notices: [GenericCreditsView.Notice] = [.init( - "Circle Icons", - "The logo is taken from the awesome Circle Icons set by Nick Roach." - ), .init( - "Country flags", - "The country flags are taken from: https://github.com/lipis/flag-icon-css/" - ), .init( - "OpenVPN", - "© Copyright 2022 OpenVPN | OpenVPN is a registered trademark of OpenVPN, Inc." - ), .init( - "WireGuard", - "© Copyright 2015-2022 Jason A. Donenfeld. All Rights Reserved. \"WireGuard\" and the \"WireGuard\" logo are registered trademarks of Jason A. Donenfeld." - )] - } - - enum About { - static let github = "GitHub" - - static let readme = "README" - - static let changelog = "CHANGELOG" - - static let faq = "FAQ" - } - - enum VPN { - static let vpn = "VPN" - - static let certificateAuthority = "CA" - - static let xor = "XOR" - } - - enum OpenVPN { - static let compLZO = "--comp-lzo" - - static let compress = "--compress" - - static let lzo = "LZO" - - enum XOR: String { - case xormask - - case xorptrpos - - case reverse - - case obfuscate - } - } - - enum Network { - static let dns = "DNS" - - static let tls = "TLS" - - static let https = "HTTPS" - - static let url = "URL" - - static let mtu = "MTU" - - static let ipv4 = "IPv4" - - static let ipv6 = "IPv6" - - static let ssid = "SSID" - - static let proxyAutoConfiguration = "PAC" - } - - enum Other { - static let siri = "Siri" - - static let iCloud = "iCloud" - - static let appleTV = "Apple TV" - - static let totp = "TOTP" - } -} - -private extension VPNProtocolType { - var keychainEntry: String { - switch self { - case .openVPN: - return "OpenVPN" - - case .wireGuard: - return "WireGuard" - } - } -} diff --git a/Passepartout/App/L10n/WireGuard+L10n.swift b/Passepartout/App/L10n/WireGuard+L10n.swift deleted file mode 100644 index fdf073f9..00000000 --- a/Passepartout/App/L10n/WireGuard+L10n.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// WireGuard+L10n.swift -// Passepartout -// -// Created by Davide De Rosa on 3/12/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary -import TunnelKitWireGuard - -extension WireGuard.ConfigurationBuilder: StyledOptionalLocalizableEntity { - public enum OptionalStyle { - case keepAlive(peerIndex: Int) - } - - public func localizedDescription(optionalStyle: OptionalStyle) -> String? { - switch optionalStyle { - case .keepAlive(let peerIndex): - return keepAlive(ofPeer: peerIndex)?.keepAliveDescription - } - } -} - -private extension UInt16 { - var keepAliveDescription: String { - // TODO: l10n, move from OpenVPN to shared - let V = L10n.Endpoint.Advanced.Openvpn.Items.self - if self > 0 { - return V.KeepAlive.Value.seconds(Int(self)) - } else { - return L10n.Global.Strings.disabled - } - } -} - -// MARK: - Errors - -extension TunnelKitWireGuardError: LocalizedError { - public var errorDescription: String? { - let V = L10n.Tunnelkit.Errors.Vpn.self - switch self { - case .dnsResolutionFailure: - return V.dns - - default: - return L10n.Global.Strings.unknown - } - } -} - -extension WireGuard.ConfigurationError: LocalizedError { -} diff --git a/Passepartout/App/LaunchScreen.storyboard b/Passepartout/App/LaunchScreen.storyboard index 055ba101..73f5f7d7 100644 --- a/Passepartout/App/LaunchScreen.storyboard +++ b/Passepartout/App/LaunchScreen.storyboard @@ -1,9 +1,9 @@ - + - + diff --git a/Passepartout/App/Mac/MacBundle.swift b/Passepartout/App/Mac/MacBundle.swift deleted file mode 100644 index 21e32e99..00000000 --- a/Passepartout/App/Mac/MacBundle.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// MacBundle.swift -// Passepartout -// -// Created by Davide De Rosa on 6/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -final class MacBundle { - static let shared = MacBundle() - - private var bridge: MacBridge! - - private lazy var bridgeDelegate = MacBundleDelegate(bundle: self) - - @MainActor - func configure() { - guard let bundleURL = Bundle.main.builtInPlugInsURL?.appendingPathComponent(Constants.Plugins.macBridgeName) else { - fatalError("Unable to find Mac bundle in plugins") - } - guard let bundle = Bundle(url: bundleURL) else { - fatalError("Unable to build Mac bundle") - } - guard let bridgeClass = bundle.principalClass as? MacBridge.Type else { - fatalError("Unable to find principal class in Mac bundle") - } - bridge = bridgeClass.init() - bridge.menu.delegate = bridgeDelegate - } - - var utils: MacUtils { - bridge.utils - } - - var menu: MacMenu { - bridge.menu - } -} diff --git a/Passepartout/App/Mac/Managers/DefaultLightProfileManager.swift b/Passepartout/App/Mac/Managers/DefaultLightProfileManager.swift deleted file mode 100644 index 97702fe2..00000000 --- a/Passepartout/App/Mac/Managers/DefaultLightProfileManager.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// DefaultLightProfileManager.swift -// Passepartout -// -// Created by Davide De Rosa on 7/3/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Combine -import Foundation -import PassepartoutLibrary - -final class DefaultLightProfile: LightProfile { - let id: UUID - - let name: String - - let vpnProtocol: String - - let isActive: Bool - - let providerName: String? - - let providerServer: LightProviderServer? - - init(_ header: Profile.Header, vpnProtocol: String, isActive: Bool, providerServer: LightProviderServer?) { - id = header.id - name = header.name - self.vpnProtocol = vpnProtocol - self.isActive = isActive - providerName = header.providerName - self.providerServer = providerServer - } -} - -final class DefaultLightProfileManager: LightProfileManager { - private let profileManager = ProfileManager.shared - - private let providerManager = ProviderManager.shared - - private var subscriptions: Set = [] - - weak var delegate: LightProfileManagerDelegate? - - init() { - profileManager.didUpdateProfiles - .receive(on: DispatchQueue.main) - .sink { [weak self] in - self?.delegate?.didUpdateProfiles() - }.store(in: &subscriptions) - } - - var hasProfiles: Bool { - profileManager.hasProfiles - } - - var profiles: [LightProfile] { - profileManager.profiles - .sorted { - $0.header < $1.header - }.map { - let server: ProviderServer? - if let serverId = $0.providerServerId { - server = providerManager.server(withId: serverId) - } else { - server = nil - } - return DefaultLightProfile( - $0.header, - vpnProtocol: $0.currentVPNProtocol.rawValue, - isActive: profileManager.isActiveProfile($0.id), - providerServer: server.map(DefaultLightProviderServer.init) - ) - } - } - - var activeProfileId: UUID? { - profileManager.activeProfileId - } - - var activeProfileName: String? { - guard let header = profileManager.headers.first(where: { - $0.id == profileManager.activeProfileId - }) else { - return nil - } - return header.name - } -} diff --git a/Passepartout/App/Mac/Managers/DefaultLightProviderManager.swift b/Passepartout/App/Mac/Managers/DefaultLightProviderManager.swift deleted file mode 100644 index 9a0363d0..00000000 --- a/Passepartout/App/Mac/Managers/DefaultLightProviderManager.swift +++ /dev/null @@ -1,125 +0,0 @@ -// -// DefaultLightProviderManager.swift -// Passepartout -// -// Created by Davide De Rosa on 7/7/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Combine -import Foundation -import PassepartoutLibrary - -final class DefaultLightProviderCategory: LightProviderCategory { - let name: String - - var locations: [LightProviderLocation] - - init(_ category: ProviderCategory) { - name = category.name - locations = category.locations - .sorted() - .map(DefaultLightProviderLocation.init) - } -} - -final class DefaultLightProviderLocation: LightProviderLocation { - let description: String - - let id: String - - let countryCode: String - - let servers: [LightProviderServer] - - init(_ location: ProviderLocation) { - description = location.localizedDescription(style: .country) - id = location.id - countryCode = location.countryCode - servers = location.servers? - .sorted() - .map(DefaultLightProviderServer.init) ?? [] - } -} - -final class DefaultLightProviderServer: LightProviderServer { - let description: String - - let longDescription: String - - let categoryName: String - - let locationId: String - - let serverId: String - - init(_ server: ProviderServer) { - description = server.localizedDescription(style: .shortWithDefault) - longDescription = server.localizedDescription(style: .longWithCategory(withCategory: false)) - categoryName = server.categoryName - locationId = server.locationId - serverId = server.id - } -} - -final class DefaultLightProviderManager: LightProviderManager { - private let providerManager = ProviderManager.shared - - private var subscriptions: Set = [] - - weak var delegate: LightProviderManagerDelegate? - - init() { - providerManager.didUpdateProviders - .receive(on: DispatchQueue.main) - .sink { [weak self] in - self?.delegate?.didUpdateProviders() - }.store(in: &subscriptions) - } - - func categories(_ name: String, vpnProtocol: String) -> [LightProviderCategory] { - guard let vpnProtocolType = VPNProtocolType(rawValue: vpnProtocol) else { - fatalError("Unrecognized VPN protocol: \(vpnProtocol)") - } - return providerManager.categories(name, vpnProtocol: vpnProtocolType) - .sorted() - .map(DefaultLightProviderCategory.init) - } - - func downloadIfNeeded(_ name: String, vpnProtocol: String) { - guard let vpnProtocolType = VPNProtocolType(rawValue: vpnProtocol) else { - fatalError("Unrecognized VPN protocol: \(vpnProtocol)") - } - guard !providerManager.isAvailable(name, vpnProtocol: vpnProtocolType) else { - return - } - Task { - do { - try await providerManager.fetchProviderPublisher( - withName: name, - vpnProtocol: vpnProtocolType, - priority: .remoteThenBundle - ).async() - } catch { - ErrorHandler.shared.handle(error) - } - } - } -} diff --git a/Passepartout/App/Mac/Managers/DefaultLightVPNManager.swift b/Passepartout/App/Mac/Managers/DefaultLightVPNManager.swift deleted file mode 100644 index ee27fd54..00000000 --- a/Passepartout/App/Mac/Managers/DefaultLightVPNManager.swift +++ /dev/null @@ -1,141 +0,0 @@ -// -// DefaultLightVPNManager.swift -// Passepartout -// -// Created by Davide De Rosa on 7/3/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Combine -import Foundation -import PassepartoutLibrary -import TunnelKitManager - -final class DefaultLightVPNManager: LightVPNManager { - private let vpnManager = VPNManager.shared - - private var subscriptions: Set = [] - - var isEnabled: Bool { - vpnManager.currentState.isEnabled - } - - var vpnStatus: LightVPNStatus { - vpnManager.currentState.vpnStatus.asLightVPNStatus - } - - private var delegates: [String: LightVPNManagerDelegate] = [:] - - init() { - vpnManager.currentState.$isEnabled - .removeDuplicates() - .receive(on: DispatchQueue.main) - .sink { [weak self] in - guard let self else { - return - } - self.didUpdateState( - isEnabled: $0, - vpnStatus: self.vpnManager.currentState.vpnStatus.asLightVPNStatus - ) - }.store(in: &subscriptions) - - vpnManager.currentState.$vpnStatus - .removeDuplicates() - .receive(on: DispatchQueue.main) - .sink { [weak self] in - guard let self else { - return - } - self.didUpdateState( - isEnabled: self.vpnManager.currentState.isEnabled, - vpnStatus: $0.asLightVPNStatus - ) - }.store(in: &subscriptions) - } - - func connect(with profileId: UUID) { - Task { - try? await vpnManager.connect(with: profileId) - } - } - - func connect(with profileId: UUID, to serverId: String) { - Task { - try? await vpnManager.connect(with: profileId, toServer: serverId) - } - } - - func disconnect() { - Task { - await vpnManager.disable() - } - } - - func toggle() { - Task { - if !isEnabled { - try? await vpnManager.connectWithActiveProfile(toServer: nil) - } else { - await vpnManager.disable() - } - } - } - - func reconnect() { - Task { - await vpnManager.reconnect() - } - } - - func addDelegate(_ delegate: LightVPNManagerDelegate, withIdentifier identifier: String) { - delegates[identifier] = delegate - } - - func removeDelegate(withIdentifier identifier: String) { - delegates.removeValue(forKey: identifier) - } -} - -extension DefaultLightVPNManager: LightVPNManagerDelegate { - func didUpdateState(isEnabled: Bool, vpnStatus: LightVPNStatus) { - delegates.values.forEach { - $0.didUpdateState(isEnabled: isEnabled, vpnStatus: vpnStatus) - } - } -} - -private extension VPNStatus { - var asLightVPNStatus: LightVPNStatus { - switch self { - case .connected: - return .connected - - case .connecting: - return .connecting - - case .disconnected: - return .disconnected - - case .disconnecting: - return .disconnecting - } - } -} diff --git a/Passepartout/App/Managers/DefaultUpgradeManagerStrategy.swift b/Passepartout/App/Managers/DefaultUpgradeManagerStrategy.swift deleted file mode 100644 index f552240e..00000000 --- a/Passepartout/App/Managers/DefaultUpgradeManagerStrategy.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// DefaultUpgradeManagerStrategy.swift -// Passepartout -// -// Created by Davide De Rosa on 3/20/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -final class DefaultUpgradeManagerStrategy: UpgradeManagerStrategy { - init() { - } - - func migrate(store: KeyValueStore, lastVersion: String?) { - - // legacy check before lastVersion was even stored - let isUpgradeFromBefore_2_2_0: Bool? = store.value(forLocation: UpgradeManager.StoreKey.existingKeyBefore_2_2_0) - if isUpgradeFromBefore_2_2_0 != nil { - pp_log.debug("Upgrading from < 2.2.0, iCloud syncing defaults to enabled") - store.setValue(true, forLocation: PersistenceManager.StoreKey.shouldEnableCloudSyncing) - store.removeValue(forLocation: UpgradeManager.StoreKey.existingKeyBefore_2_2_0) - } - - guard let lastVersion else { - pp_log.debug("Fresh install") - return - } - pp_log.debug("Upgrade from \(lastVersion)") - } - - func migrateData() { - } -} diff --git a/Passepartout/App/Managers/IntentsManager.swift b/Passepartout/App/Managers/IntentsManager.swift deleted file mode 100644 index c36acd76..00000000 --- a/Passepartout/App/Managers/IntentsManager.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// IntentsManager.swift -// Passepartout -// -// Created by Davide De Rosa on 3/30/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Combine -import Foundation -@preconcurrency import Intents -import IntentsUI -import PassepartoutLibrary - -@MainActor -final class IntentsManager: NSObject, ObservableObject { - @Published private(set) var isReloadingShortcuts = false - - @Published private(set) var shortcuts: [UUID: Shortcut] = [:] - - let shouldDismissIntentView = PassthroughSubject() - - private var continuation: CheckedContinuation<[INVoiceShortcut], Never>? - - override init() { - super.init() - Task { - await reloadShortcuts() - } - } - - func reloadShortcuts() async { - isReloadingShortcuts = true - do { - let vs = try await INVoiceShortcutCenter.shared.allVoiceShortcuts() - shortcuts = vs.reduce(into: [:]) { - $0[$1.identifier] = Shortcut($1) - } - isReloadingShortcuts = false - } catch { - pp_log.error("Unable to fetch existing shortcuts: \(error)") - isReloadingShortcuts = false - } - } -} - -extension IntentsManager: INUIAddVoiceShortcutViewControllerDelegate { - func addVoiceShortcutViewController(_ controller: INUIAddVoiceShortcutViewController, didFinishWith voiceShortcut: INVoiceShortcut?, error: Error?) { - guard let vs = voiceShortcut else { - shouldDismissIntentView.send() - return - } - shortcuts[vs.identifier] = Shortcut(vs) - shouldDismissIntentView.send() - } - - func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) { - shouldDismissIntentView.send() - } -} - -extension IntentsManager: INUIEditVoiceShortcutViewControllerDelegate { - func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didUpdate voiceShortcut: INVoiceShortcut?, error: Error?) { - guard let vs = voiceShortcut else { - return - } - - shortcuts[vs.identifier] = Shortcut(vs) - shouldDismissIntentView.send() - - // XXX: iOS bug, vs.invocationPhrase here is still the old one before edit - // - // additionally, back from edit view controller does not trigger either onAppear or - // scenePhase .active FFS - // - // so damn it, reload manually after a delay - Task { - await Task.maybeWait(forMilliseconds: 200) - await reloadShortcuts() - } - } - - func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID) { - shortcuts.removeValue(forKey: deletedVoiceShortcutIdentifier) - shouldDismissIntentView.send() - } - - func editVoiceShortcutViewControllerDidCancel(_ controller: INUIEditVoiceShortcutViewController) { - shouldDismissIntentView.send() - } -} -#endif diff --git a/Passepartout/App/Managers/PersistenceManager.swift b/Passepartout/App/Managers/PersistenceManager.swift deleted file mode 100644 index 927f85dc..00000000 --- a/Passepartout/App/Managers/PersistenceManager.swift +++ /dev/null @@ -1,198 +0,0 @@ -// -// PersistenceManager.swift -// Passepartout -// -// Created by Davide De Rosa on 4/6/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import CloudKit -import Combine -import CoreData -import Foundation -import PassepartoutLibrary - -@MainActor -final class PersistenceManager: ObservableObject { - let store: KeyValueStore - - 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 { - didSet { - pp_log.info("CloudKit enabled: \(isCloudSyncingEnabled)") - didChangePersistence.send() - } - } - - @Published private(set) var isErasingCloudKitStore = false - - let didChangePersistence = PassthroughSubject() - - init(store: KeyValueStore, - ckContainerId: String, - ckSharedContainerId: String, - ckCoreDataZone: String) { - self.store = store - self.ckContainerId = ckContainerId - self.ckSharedContainerId = ckSharedContainerId - self.ckCoreDataZone = ckCoreDataZone - isCloudSyncingEnabled = store.canEnableCloudSyncing - - // set once - if persistenceAuthor == nil { - persistenceAuthor = UUID().uuidString - } - } - - func loadVPNPersistence(withName containerName: String) -> VPNPersistence { - 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 - return persistence - } -} - -// MARK: CloudKit - -extension PersistenceManager { - func eraseCloudKitStore() async { - isErasingCloudKitStore = true - await Self.eraseCloudKitStore( - fromContainerWithId: ckContainerId, - zoneId: .init(zoneName: ckCoreDataZone) - ) - await Self.eraseCloudKitStore( - fromContainerWithId: ckSharedContainerId, - zoneId: .init(zoneName: ckCoreDataZone) - ) - isErasingCloudKitStore = false - } - - // WARNING: this is not running on main actor - private static func eraseCloudKitStore(fromContainerWithId containerId: String, zoneId: CKRecordZone.ID) async { - do { - let container = CKContainer(identifier: containerId) - let db = container.privateCloudDatabase - try await db.deleteRecordZone(withID: zoneId) - } catch { - pp_log.error("Unable to erase CloudKit store: \(error)") - } - } -} - -// MARK: KeyValueStore - -private extension KeyValueStore { - private var cloudKitToken: Any? { - FileManager.default.ubiquityIdentityToken - } - - private var isCloudKitSupported: Bool { - #if !os(tvOS) - cloudKitToken != nil - #else - true - #endif - } - - var canEnableCloudSyncing: Bool { - isCloudKitSupported && shouldEnableCloudSyncing - } - - var shouldEnableCloudSyncing: Bool { - get { - value(forLocation: PersistenceManager.StoreKey.shouldEnableCloudSyncing) ?? false - } - set { - setValue(newValue, forLocation: PersistenceManager.StoreKey.shouldEnableCloudSyncing) - } - } -} - -extension PersistenceManager { - private(set) var persistenceAuthor: String? { - get { - store.value(forLocation: StoreKey.persistenceAuthor) - } - set { - store.setValue(newValue, forLocation: StoreKey.persistenceAuthor) - } - } - - var shouldEnableCloudSyncing: Bool { - get { - store.shouldEnableCloudSyncing - } - set { - objectWillChange.send() - store.shouldEnableCloudSyncing = newValue - - // iCloud may be externally disabled from the device settings - let newIsCloudSyncingEnabled = store.canEnableCloudSyncing - guard newIsCloudSyncingEnabled != isCloudSyncingEnabled else { - pp_log.debug("CloudKit state did not change") - return - } - isCloudSyncingEnabled = newIsCloudSyncingEnabled - } - } -} - -// TODO: iCloud, restore private after dropping migration from 2.2.0 -// private extension PersistenceManager { -extension PersistenceManager { - enum StoreKey: String, KeyStoreDomainLocation { - case persistenceAuthor - - case shouldEnableCloudSyncing - - var domain: String { - "Passepartout.PersistenceManager" - } - } -} diff --git a/Passepartout/App/Managers/UpgradeManager.swift b/Passepartout/App/Managers/UpgradeManager.swift deleted file mode 100644 index 3d003a00..00000000 --- a/Passepartout/App/Managers/UpgradeManager.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// UpgradeManager.swift -// Passepartout -// -// Created by Davide De Rosa on 2/8/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -@MainActor -final class UpgradeManager: ObservableObject { - - // MARK: Initialization - - private let store: KeyValueStore - - private let strategy: UpgradeManagerStrategy - - // MARK: State - - @Published private(set) var isDoingMigrations = true - - init( - store: KeyValueStore, - strategy: UpgradeManagerStrategy - ) { - self.store = store - self.strategy = strategy - } - - func migrate(toVersion currentVersion: String) { - if let lastVersion, currentVersion > lastVersion { - strategy.migrate(store: store, lastVersion: lastVersion) - } - lastVersion = currentVersion - } - - func migrateData(profileManager: ProfileManager) { - isDoingMigrations = false - } -} - -// MARK: KeyValueStore - -private extension UpgradeManager { - var lastVersion: String? { - get { - store.value(forLocation: StoreKey.lastVersion) - } - set { - store.setValue(newValue, forLocation: StoreKey.lastVersion) - } - } -} - -extension UpgradeManager { - enum StoreKey: String, KeyStoreDomainLocation { - - @available(*, deprecated, message: "Retain temporarily for migrations") - case existingKeyBefore_2_2_0 = "didMigrateToV2" - - case lastVersion - - var domain: String { - "Passepartout.UpgradeManager" - } - } -} diff --git a/Passepartout/App/PassepartoutApp.swift b/Passepartout/App/PassepartoutApp.swift index 88fc3dc4..98515434 100644 --- a/Passepartout/App/PassepartoutApp.swift +++ b/Passepartout/App/PassepartoutApp.swift @@ -2,7 +2,7 @@ // PassepartoutApp.swift // Passepartout // -// Created by Davide De Rosa on 12/28/21. +// Created by Davide De Rosa on 2/22/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,44 +23,48 @@ // along with Passepartout. If not, see . // -import PassepartoutLibrary +import AppLibrary +import CommonLibrary +import PassepartoutKit import SwiftUI @main struct PassepartoutApp: App { - @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate - @SceneBuilder var body: some Scene { +#if os(iOS) + @UIApplicationDelegateAdaptor + private var appDelegate: AppDelegate +#else + @NSApplicationDelegateAdaptor + private var appDelegate: AppDelegate +#endif + + private let context: AppContext = .shared +// private let context: AppContext = .mock(withRegistry: .shared) + + @StateObject + private var theme = Theme() + + var body: some Scene { WindowGroup { - MainView() - #if !os(tvOS) - .withoutTitleBar() - .onIntentActivity(IntentDispatcher.connectVPN) - .onIntentActivity(IntentDispatcher.disableVPN) - .onIntentActivity(IntentDispatcher.enableVPN) - .onIntentActivity(IntentDispatcher.moveToLocation) - .onIntentActivity(IntentDispatcher.trustCellularNetwork) - .onIntentActivity(IntentDispatcher.trustCurrentNetwork) - .onIntentActivity(IntentDispatcher.untrustCellularNetwork) - .onIntentActivity(IntentDispatcher.untrustCurrentNetwork) - #endif - } - } -} - -private extension View { - - @MainActor - func onIntentActivity(_ activity: IntentActivity) -> some View { - onContinueUserActivity(activity.name) { userActivity in - - // eligibility: ignore Siri shortcuts if not purchased - guard ProductManager.shared.isEligible(forFeature: .siriShortcuts) else { - pp_log.warning("Ignore activity handler, not eligible for Siri shortcuts") - return + AppCoordinator( + profileManager: context.profileManager, + tunnel: context.tunnel, + registry: context.registry + ) + .onLoad { + CommonLibrary.configureLogging( + to: Constants.shared.urlForAppLog, + parameters: Constants.shared.log + ) + AppLibrary.configure(with: context) } - pp_log.info("Handling activity: \(activity.name)") - activity.handler(userActivity, .shared) + .environmentObject(theme) + .environmentObject(context.iapManager) + .environmentObject(context.connectionObserver) } +#if os(macOS) + .defaultSize(width: 600.0, height: 400.0) +#endif } } diff --git a/Passepartout/App/Providers.xcassets/Contents.json b/Passepartout/App/Providers.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/Passepartout/App/Providers.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/Contents.json b/Passepartout/App/Providers.xcassets/providers/Contents.json deleted file mode 100644 index 6e965652..00000000 --- a/Passepartout/App/Providers.xcassets/providers/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/hideme.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/hideme.imageset/Contents.json deleted file mode 100644 index 1fd99117..00000000 --- a/Passepartout/App/Providers.xcassets/providers/hideme.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "hideme@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "hideme@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/hideme.imageset/hideme@2x.png b/Passepartout/App/Providers.xcassets/providers/hideme.imageset/hideme@2x.png deleted file mode 100644 index 923e1849..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/hideme.imageset/hideme@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/hideme.imageset/hideme@3x.png b/Passepartout/App/Providers.xcassets/providers/hideme.imageset/hideme@3x.png deleted file mode 100644 index 57d1ff1a..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/hideme.imageset/hideme@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/Contents.json deleted file mode 100644 index e1492758..00000000 --- a/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "ivpn@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "ivpn@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/ivpn@2x.png b/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/ivpn@2x.png deleted file mode 100644 index 50a6ae77..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/ivpn@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/ivpn@3x.png b/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/ivpn@3x.png deleted file mode 100644 index 62849540..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/ivpn.imageset/ivpn@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/Contents.json deleted file mode 100644 index 89ebd272..00000000 --- a/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "mullvad@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "mullvad@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/mullvad@2x.png b/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/mullvad@2x.png deleted file mode 100644 index 2ede91d9..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/mullvad@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/mullvad@3x.png b/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/mullvad@3x.png deleted file mode 100644 index 4fc64944..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/mullvad.imageset/mullvad@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/Contents.json deleted file mode 100644 index c088a073..00000000 --- a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/Contents.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "1x", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ] - }, - { - "idiom" : "universal", - "filename" : "nordvpn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nordvpn-dark@2x.png", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "nordvpn@3x.png", - "scale" : "3x" - }, - { - "idiom" : "universal", - "filename" : "nordvpn-dark@3x.png", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn-dark@2x.png b/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn-dark@2x.png deleted file mode 100644 index 2703a4dd..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn-dark@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn-dark@3x.png b/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn-dark@3x.png deleted file mode 100644 index 7f0af2ef..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn-dark@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn@2x.png b/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn@2x.png deleted file mode 100644 index a8db1e30..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn@3x.png b/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn@3x.png deleted file mode 100644 index 971a5361..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/nordvpn.imageset/nordvpn@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/oeck.imageset/Contents.json deleted file mode 100644 index e6f2b42a..00000000 --- a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/Contents.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "oeck@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "oeck-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "oeck@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "oeck-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck-dark@2x.png b/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck-dark@2x.png deleted file mode 100644 index ab14971a..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck-dark@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck-dark@3x.png b/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck-dark@3x.png deleted file mode 100644 index 1048315f..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck-dark@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck@2x.png b/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck@2x.png deleted file mode 100644 index 243e3093..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck@3x.png b/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck@3x.png deleted file mode 100644 index 5dd4bc54..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/oeck.imageset/oeck@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/pia.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/pia.imageset/Contents.json deleted file mode 100644 index 8a2923a3..00000000 --- a/Passepartout/App/Providers.xcassets/providers/pia.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "pia@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "pia@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/pia.imageset/pia@2x.png b/Passepartout/App/Providers.xcassets/providers/pia.imageset/pia@2x.png deleted file mode 100644 index 92ce04c6..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/pia.imageset/pia@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/pia.imageset/pia@3x.png b/Passepartout/App/Providers.xcassets/providers/pia.imageset/pia@3x.png deleted file mode 100644 index 97c08e84..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/pia.imageset/pia@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/Contents.json deleted file mode 100644 index 13f500e4..00000000 --- a/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "placeholder@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "placeholder@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/placeholder@2x.png b/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/placeholder@2x.png deleted file mode 100644 index 3117a662..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/placeholder@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/placeholder@3x.png b/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/placeholder@3x.png deleted file mode 100644 index 1dc8aed7..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/placeholder.imageset/placeholder@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/Contents.json deleted file mode 100644 index 148aaa0a..00000000 --- a/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "protonvpn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "protonvpn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/protonvpn@2x.png b/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/protonvpn@2x.png deleted file mode 100644 index f5b03522..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/protonvpn@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/protonvpn@3x.png b/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/protonvpn@3x.png deleted file mode 100644 index c25f8ded..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/protonvpn.imageset/protonvpn@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/Contents.json deleted file mode 100644 index 901b3e5a..00000000 --- a/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "surfshark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "surfshark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/surfshark@2x.png b/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/surfshark@2x.png deleted file mode 100644 index 701f3a1f..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/surfshark@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/surfshark@3x.png b/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/surfshark@3x.png deleted file mode 100644 index 62c832c1..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/surfshark.imageset/surfshark@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/torguard.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/torguard.imageset/Contents.json deleted file mode 100644 index 69d95d66..00000000 --- a/Passepartout/App/Providers.xcassets/providers/torguard.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "torguard@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "torguard@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Passepartout/App/Providers.xcassets/providers/torguard.imageset/torguard@2x.png b/Passepartout/App/Providers.xcassets/providers/torguard.imageset/torguard@2x.png deleted file mode 100644 index 32710a2d..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/torguard.imageset/torguard@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/torguard.imageset/torguard@3x.png b/Passepartout/App/Providers.xcassets/providers/torguard.imageset/torguard@3x.png deleted file mode 100644 index de3a90da..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/torguard.imageset/torguard@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/Contents.json deleted file mode 100644 index d3910b1a..00000000 --- a/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "tunnelbear@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "tunnelbear@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/tunnelbear@2x.png b/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/tunnelbear@2x.png deleted file mode 100644 index b7d3bb4c..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/tunnelbear@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/tunnelbear@3x.png b/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/tunnelbear@3x.png deleted file mode 100644 index 973baf5c..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/tunnelbear.imageset/tunnelbear@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/Contents.json deleted file mode 100644 index 34292c4b..00000000 --- a/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "vyprvpn@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "vyprvpn@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/vyprvpn@2x.png b/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/vyprvpn@2x.png deleted file mode 100644 index 07db8402..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/vyprvpn@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/vyprvpn@3x.png b/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/vyprvpn@3x.png deleted file mode 100644 index 1ee302b2..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/vyprvpn.imageset/vyprvpn@3x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/Contents.json b/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/Contents.json deleted file mode 100644 index 63da3f6c..00000000 --- a/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "windscribe@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "windscribe@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/windscribe@2x.png b/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/windscribe@2x.png deleted file mode 100644 index b6cc4e90..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/windscribe@2x.png and /dev/null differ diff --git a/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/windscribe@3x.png b/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/windscribe@3x.png deleted file mode 100644 index 9987b8ad..00000000 Binary files a/Passepartout/App/Providers.xcassets/providers/windscribe.imageset/windscribe@3x.png and /dev/null differ diff --git a/Passepartout/App/Reusable/ActivityView.swift b/Passepartout/App/Reusable/ActivityView.swift deleted file mode 100644 index 41aa32c8..00000000 --- a/Passepartout/App/Reusable/ActivityView.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// ActivityView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import SwiftUI -import UIKit - -struct ActivityView: UIViewControllerRepresentable { - let activityItems: [Any] - - let applicationActivities: [UIActivity]? = nil - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIActivityViewController { - UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities) - } - - func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext) { - } -} -#endif diff --git a/Passepartout/App/Reusable/CopySavingButton.swift b/Passepartout/App/Reusable/CopySavingButton.swift deleted file mode 100644 index 545b0bf4..00000000 --- a/Passepartout/App/Reusable/CopySavingButton.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// CopySavingButton.swift -// Passepartout -// -// Created by Davide De Rosa on 4/6/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -struct CopySavingButton: View { - @Binding var original: T - - @Binding var copy: T - - var mapping: (T) -> T - - let label: () -> Label - - var saveAnyway = false - - var onSave: (() -> Void)? - - @State private var isLoaded = false - - var body: some View { - Button(action: saveToOriginal, label: label) - .disabled(!canSave) - .onAppear { - loadFromOriginal(once: true) - }.onChange(of: original) { _ in - withAnimation { - loadFromOriginal(once: false) - } - } - } -} - -// MARK: - - -private extension CopySavingButton { - var canSave: Bool { - isLoaded && (saveAnyway || copy != original) - } -} - -// MARK: - - -private extension CopySavingButton { - func loadFromOriginal(once: Bool) { - guard !once || !isLoaded else { - return - } - copy = original - isLoaded = true - } - - func saveToOriginal() { - if copy != original { - original = copy - } - onSave?() - } -} diff --git a/Passepartout/App/Reusable/EditableTextList.swift b/Passepartout/App/Reusable/EditableTextList.swift deleted file mode 100644 index 74141340..00000000 --- a/Passepartout/App/Reusable/EditableTextList.swift +++ /dev/null @@ -1,215 +0,0 @@ -// -// EditableTextList.swift -// Passepartout -// -// Created by Davide De Rosa on 3/31/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -struct IdentifiableString: Identifiable, Equatable { - var id = UUID() - - var string: String -} - -struct EditableTextFieldCallback { - let isNewElement: Bool - - let text: Binding - - let onEditingChanged: (Bool) -> Void - - let onCommit: () -> Void -} - -struct EditableTextList: View { - @Binding var elements: [String] - - var allowsDuplicates = true - - var mapping: ([IdentifiableString]) -> [IdentifiableString] = { $0 } - - var onAdd: ((Binding) -> Void)? - - let textField: (EditableTextFieldCallback) -> Field - - let addLabel: () -> ActionLabel - - var commitLabel: (() -> ActionLabel)? - - @State private var isLoaded = false - - @State private var identifiableElements: [IdentifiableString] = [] - - @State private var editedTextStrings: [UUID: String] = [:] - - private let addedUUID = UUID() - - var body: some View { - debugChanges() - return Group { - ForEach(mapping(identifiableElements), content: existingRow) - .onDelete(perform: onDelete) - .onMove(perform: onMove) - - newRow - }.onAppear { - guard !isLoaded else { - return - } - isLoaded = true - identifiableElements = elements.map { - IdentifiableString(string: $0) - } - }.onChange(of: elements, perform: remapElements) - } -} - -// MARK: - - -private extension EditableTextList { - var addedText: Binding { - .init { - editedTextStrings[addedUUID] ?? "" - } set: { - editedTextStrings[addedUUID] = $0 - } - } - - func existingRow(_ element: IdentifiableString) -> some View { - let editedText = binding(toEditedElement: element) - - return textField(.init(isNewElement: false, text: editedText, onEditingChanged: { - if $0 { - editedTextStrings.removeValue(forKey: element.id) -// print(">>> editing: '\(text.wrappedValue.string)' (\(text.wrappedValue.id))") - } - }, onCommit: { - replaceElement(at: element.id, with: editedText) - })) - } - - var newRow: some View { - AddingTextField( - onAdd: { - addedText.wrappedValue = "" - onAdd?(addedText) - }, - onCommit: addElement, - textField: { - textField(.init(isNewElement: true, text: addedText, onEditingChanged: { _ in }, onCommit: $0)) - }, - addLabel: addLabel, - commitLabel: commitLabel - ) - } -} - -private extension EditableTextList { - func remapElements(_ newElements: [String]) { - var oldIdentifiableElements = identifiableElements - var newIdentifiableElements: [IdentifiableString] = [] - - newElements.forEach { newString in - let id: UUID - if let found = oldIdentifiableElements.firstIndex(where: { - $0.string == newString - }) { - id = oldIdentifiableElements[found].id - oldIdentifiableElements.remove(at: found) - } else { - id = UUID() - } - newIdentifiableElements.append(.init(id: id, string: newString)) - } - - guard newIdentifiableElements != identifiableElements else { - return - } - withAnimation { - identifiableElements = newIdentifiableElements - } - } - - func binding(toEditedElement element: IdentifiableString) -> Binding { -// print(">>> <-> \(element)") - .init { - editedTextStrings[element.id] ?? element.string - } set: { - editedTextStrings[element.id] = $0 - } - } -} - -// MARK: - - -private extension EditableTextList { - func addElement() { - guard allowsDuplicates || !identifiableElements.contains(where: { - $0.string == addedText.wrappedValue - }) else { - return - } -// print(">>> + \(addedElement.wrappedValue)") - identifiableElements.append(.init(string: addedText.wrappedValue)) - commit() - } - - func replaceElement(at id: UUID, with editedText: Binding) { -// print(">>> \(identifiableElements[id].string) -> \(editedText.wrappedValue)") - guard let i = identifiableElements.firstIndex(where: { - $0.id == id - }) else { - assertionFailure("Editing removed element?") - return - } - guard allowsDuplicates || !identifiableElements.contains(where: { - $0.string == editedText.wrappedValue - }) else { - editedText.wrappedValue = identifiableElements[i].string - return - } - withAnimation { - identifiableElements[i].string = editedText.wrappedValue - } - commit() - } - - func onDelete(offsets: IndexSet) { - var mapped = mapping(identifiableElements) - mapped.remove(atOffsets: offsets) - identifiableElements = mapped - commit() - } - - func onMove(indexSet: IndexSet, to offset: Int) { - var mapped = mapping(identifiableElements) - mapped.move(fromOffsets: indexSet, toOffset: offset) - identifiableElements = mapped - commit() - } - - func commit() { -// print(">>> identifiableElements = \(identifiableElements.map { "\($0.string) (\($0.id))" })") - elements = identifiableElements.map(\.string) - } -} diff --git a/Passepartout/App/Reusable/ErrorHandler.swift b/Passepartout/App/Reusable/ErrorHandler.swift deleted file mode 100644 index 7accbb62..00000000 --- a/Passepartout/App/Reusable/ErrorHandler.swift +++ /dev/null @@ -1,77 +0,0 @@ -import SwiftUI - -// https://www.ralfebert.com/swiftui/generic-error-handling/ - -private struct ErrorAlert: Identifiable { - let id = UUID() - - let title: String? - - let message: String - - let dismissAction: (() -> Void)? -} - -@MainActor -final class ErrorHandler: ObservableObject { - static let shared = ErrorHandler() - - @Published fileprivate var isPresented = false - - @Published fileprivate var currentAlert: ErrorAlert? - - func handle(_ error: Error, title: String? = nil, onDismiss: (() -> Void)? = nil) { - currentAlert = ErrorAlert( - title: title, - message: AppError(error).localizedDescription, - dismissAction: onDismiss - ) - isPresented = true - } - - func handle(title: String, message: String, onDismiss: (() -> Void)? = nil) { - currentAlert = ErrorAlert( - title: title, - message: message, - dismissAction: onDismiss - ) - isPresented = true - } -} - -struct HandleErrorsByShowingAlertViewModifier: ViewModifier { - @ObservedObject private var errorHandler: ErrorHandler - - init() { - errorHandler = .shared - } - - func body(content: Content) -> some View { - content - // Applying the alert for error handling using a background element - // is a workaround, if the alert would be applied directly, - // other .alert modifiers inside of content would not work anymore - .background( - EmptyView() - .alert( - errorHandler.currentAlert?.title ?? Unlocalized.appName, - isPresented: $errorHandler.isPresented, - presenting: errorHandler.currentAlert - ) { alert in - Button(role: .cancel) { - alert.dismissAction?() - } label: { - Text(L10n.Global.Strings.ok) - } - } message: { alert in - Text(alert.message.withTrailingDot) - } - ) - } -} - -extension View { - func withErrorHandler() -> some View { - modifier(HandleErrorsByShowingAlertViewModifier()) - } -} diff --git a/Passepartout/App/Reusable/IntentAddView.swift b/Passepartout/App/Reusable/IntentAddView.swift deleted file mode 100644 index c56a1cc2..00000000 --- a/Passepartout/App/Reusable/IntentAddView.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// IntentAddView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/13/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Intents -import IntentsUI -import SwiftUI - -struct IntentAddView: UIViewControllerRepresentable { - let shortcut: INShortcut - - let delegate: INUIAddVoiceShortcutViewControllerDelegate? - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> INUIAddVoiceShortcutViewController { - let vc = INUIAddVoiceShortcutViewController(shortcut: shortcut) - vc.delegate = delegate - return vc - } - - func updateUIViewController(_ uiViewController: INUIAddVoiceShortcutViewController, context: UIViewControllerRepresentableContext) { - } -} -#endif diff --git a/Passepartout/App/Reusable/IntentEditView.swift b/Passepartout/App/Reusable/IntentEditView.swift deleted file mode 100644 index d21bc7bd..00000000 --- a/Passepartout/App/Reusable/IntentEditView.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// IntentEditView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/13/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Intents -import IntentsUI -import SwiftUI - -struct IntentEditView: UIViewControllerRepresentable { - let shortcut: Shortcut - - let delegate: INUIEditVoiceShortcutViewControllerDelegate? - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> INUIEditVoiceShortcutViewController { - let vc = INUIEditVoiceShortcutViewController(voiceShortcut: shortcut.native) - vc.delegate = delegate - return vc - } - - func updateUIViewController(_ uiViewController: INUIEditVoiceShortcutViewController, context: UIViewControllerRepresentableContext) { - } -} -#endif diff --git a/Passepartout/App/Reusable/LockableView.swift b/Passepartout/App/Reusable/LockableView.swift deleted file mode 100644 index 4f4f4d41..00000000 --- a/Passepartout/App/Reusable/LockableView.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// LockableView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/20/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -struct LockableView: View { - @Binding var locksInBackground: Bool - - let content: () -> Content - - let lockedContent: () -> LockedContent - - let unlockBlock: () async -> Bool - - @Environment(\.scenePhase) private var scenePhase - - @ObservedObject private var lock: Lock = .shared - - @Binding private var state: Lock.State - - init( - locksInBackground: Binding, - content: @escaping () -> Content, - lockedContent: @escaping () -> LockedContent, - unlockBlock: @escaping () async -> Bool - ) { - _locksInBackground = locksInBackground - self.content = content - self.lockedContent = lockedContent - self.unlockBlock = unlockBlock - - _state = .init { - Lock.shared.state - } set: { - Lock.shared.state = $0 - } - } - - var body: some View { - ZStack { - content() - if locksInBackground && state != .none { - lockedContent() - } - }.onChange(of: scenePhase, perform: onScenePhase) - } -} - -// MARK: - - -private final class Lock: ObservableObject { - enum State { - case none - - case covered - - case locked - } - - static let shared = Lock() - - @Published var state: State = .locked - - private init() { - } -} - -// MARK: - - -private extension LockableView { - func onScenePhase(_ scenePhase: ScenePhase) { - switch scenePhase { - case .active: - unlockIfNeeded() - - case .inactive: - if state == .none { - state = .covered - } - - case .background: - lockIfNeeded() - - default: - break - } - } - - func lockIfNeeded() { - guard locksInBackground else { - return - } - state = .locked - } - - func unlockIfNeeded() { - guard locksInBackground else { - state = .none - return - } - switch state { - case .none: - break - - case .covered: - state = .none - - case .locked: - Task { @MainActor in - guard await unlockBlock() else { - return - } - state = .none - } - } - } -} diff --git a/Passepartout/App/Reusable/RevealingSecureField.swift b/Passepartout/App/Reusable/RevealingSecureField.swift deleted file mode 100644 index 289e311e..00000000 --- a/Passepartout/App/Reusable/RevealingSecureField.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// RevealingSecureField.swift -// Passepartout -// -// Created by Davide De Rosa on 3/22/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -struct RevealingSecureField: View { - let title: String - - @Binding private var text: String - - private let conceilImage: () -> ImageContent - - private let revealImage: () -> ImageContent - - @State private var isRevealed = false - - init( - _ title: String, - text: Binding, - conceilImage: @escaping () -> ImageContent, - revealImage: @escaping () -> ImageContent - ) { - self.title = title - _text = text - self.conceilImage = conceilImage - self.revealImage = revealImage - } - - var body: some View { - HStack { - if isRevealed { - TextField(title, text: $text) - Spacer() - Button { - isRevealed.toggle() - } label: { - conceilImage() - }.buttonStyle(.plain) - } else { - SecureField(title, text: $text) - Spacer() - Button { - isRevealed.toggle() - } label: { - revealImage() - }.buttonStyle(.plain) - } - } - } -} diff --git a/Passepartout/App/Reusable/Reviewer.swift b/Passepartout/App/Reusable/Reviewer.swift deleted file mode 100644 index 632b13b1..00000000 --- a/Passepartout/App/Reusable/Reviewer.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// Reviewer.swift -// Passepartout -// -// Created by Davide De Rosa on 9/9/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import StoreKit -import UIKit - -public final class Reviewer: ObservableObject { - private struct Keys { - static let eventCount = "Reviewer.EventCount" - - static let lastVersion = "Reviewer.LastVersion" - } - - private let defaults: UserDefaults - - public var eventCountBeforeRating: Int = .max - - public init() { - defaults = .standard - } - - @discardableResult - public func reportEvent() -> Bool { - reportEvents(1) - } - - @discardableResult - public func reportEvents(_ eventCount: Int, appStoreId: String? = nil) -> Bool { - guard let currentVersionString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String, let currentVersion = Int(currentVersionString) else { - return false - } - let lastVersion = defaults.integer(forKey: Keys.lastVersion) - if lastVersion > 0 { - print("Reviewer: App last reviewed for version \(lastVersion)") - } else { - print("Reviewer: App was never reviewed") - } - guard currentVersion != lastVersion else { - print("Reviewer: App already reviewed for version \(currentVersion)") - return false - } - - var count = defaults.integer(forKey: Keys.eventCount) - count += eventCount - defaults.set(count, forKey: Keys.eventCount) - print("Reviewer: Event reported for version \(currentVersion) (count: \(count), prompt: \(eventCountBeforeRating))") - - guard count >= eventCountBeforeRating else { - return false - } - print("Reviewer: Prompting for review...") - - 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")! - } -} diff --git a/Passepartout/App/Reusable/StyledPicker.swift b/Passepartout/App/Reusable/StyledPicker.swift deleted file mode 100644 index 206b4121..00000000 --- a/Passepartout/App/Reusable/StyledPicker.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// StyledPicker.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -struct StyledPicker: View { - let title: String - - @Binding var selection: T - - let values: [T] - - let pickerLabel: (T) -> Label - - let selectionLabel: (T) -> Label - - let listStyle: () -> Style - - @State private var isPresented = false - - var body: some View { - NavigationLink(isActive: $isPresented, destination: pickerView) { - HStack { - Text(title) - Spacer() - selectionLabel(selection) - } - } - } -} - -// MARK: - - -private extension StyledPicker { - func pickerView() -> some View { - List { - Section { - ForEach(values, id: \.self) { value in - Button { - selection = value - isPresented = false - } label: { - HStack { - pickerLabel(value) - Spacer() - if value == selection { - Image(systemName: "checkmark") - } - } - } - } - } - }.navigationTitle(title) - .listStyle(listStyle()) - } -} diff --git a/Passepartout/App/SceneDelegate+Shortcuts.swift b/Passepartout/App/SceneDelegate+Shortcuts.swift deleted file mode 100644 index c17085f7..00000000 --- a/Passepartout/App/SceneDelegate+Shortcuts.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// SceneDelegate+Shortcuts.swift -// Passepartout -// -// Created by Davide De Rosa on 3/19/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI - -enum ShortcutType: String { - case enableVPN - - case disableVPN - - case reconnectVPN -} - -extension SceneDelegate { - func rebuildShortcutItems() { - let items: [UIApplicationShortcutItem] - if VPNManager.shared.currentState.isEnabled { - items = [ - ShortcutType.disableVPN.shortcutItem(withTitle: L10n.Shortcuts.Add.Items.DisableVpn.caption), - ShortcutType.reconnectVPN.shortcutItem(withTitle: L10n.Global.Strings.reconnect) - ] - } else if ProfileManager.shared.hasActiveProfile { - items = [ - ShortcutType.enableVPN.shortcutItem(withTitle: L10n.Shortcuts.Add.Items.EnableVpn.caption) - ] - } else { - items = [] - } - UIApplication.shared.shortcutItems = items - } - - func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem) { - switch shortcutItem.type { - case ShortcutType.enableVPN.rawValue: - Task { - do { - try await VPNManager.shared.connectWithActiveProfile(toServer: nil) - } catch { - ErrorHandler.shared.handle(error) - } - } - - case ShortcutType.disableVPN.rawValue: - Task { - await VPNManager.shared.disable() - } - - case ShortcutType.reconnectVPN.rawValue: - Task { - await VPNManager.shared.reconnect() - } - - default: - break - } - } -} - -private extension ShortcutType { - func shortcutItem(withTitle title: String) -> UIApplicationShortcutItem { - UIApplicationShortcutItem( - type: rawValue, - localizedTitle: title, - localizedSubtitle: nil, - icon: .init(systemImageName: themeImageName) - ) - } -} -#endif diff --git a/Passepartout/App/SceneDelegate.swift b/Passepartout/App/SceneDelegate.swift deleted file mode 100644 index 5cf84f88..00000000 --- a/Passepartout/App/SceneDelegate.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// SceneDelegate.swift -// Passepartout -// -// Created by Davide De Rosa on 3/19/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -@MainActor -final class SceneDelegate: UIResponder, UIWindowSceneDelegate { - func sceneDidEnterBackground(_ scene: UIScene) { - ProfileManager.shared.persist() - #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 -} diff --git a/Passepartout/App/Scripts/build_wireguard_go_bridge.sh b/Passepartout/App/Scripts/build_wireguard_go_bridge.sh deleted file mode 100755 index ac097f81..00000000 --- a/Passepartout/App/Scripts/build_wireguard_go_bridge.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -# build_wireguard_go_bridge.sh - Builds WireGuardKitGo -# -# Figures out the directory where the wireguard-apple SPM package -# is checked out by Xcode (so that it works when building as well as -# archiving), then cd-s to the WireGuardKitGo directory -# and runs make there. - -project_data_dir="$BUILD_DIR" - -# The wireguard-apple README suggests using ${BUILD_DIR%Build/*}, which -# doesn't seem to work. So here, we do the equivalent in script. - -while true; do - parent_dir=$(dirname "$project_data_dir") - basename=$(basename "$project_data_dir") - project_data_dir="$parent_dir" - if [ "$basename" = "Build" ]; then - break - fi -done - -# The wireguard-apple README looks into -# SourcePackages/checkouts/wireguard-apple, but Xcode seems to place the -# sources in SourcePackages/checkouts/ so just playing it safe and -# trying both. - -checkouts_dir="$project_data_dir"/SourcePackages/checkouts -if [ -e "$checkouts_dir"/wireguard-apple ]; then - checkouts_dir="$checkouts_dir"/wireguard-apple -fi - -wireguard_go_dir="$checkouts_dir"/Sources/WireGuardKitGo - -# To ensure we have Go in our path, we add where -# Homebrew generally installs executables -export PATH=${PATH}:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin - -cd "$wireguard_go_dir" && /usr/bin/make diff --git a/Passepartout/App/Scripts/copy_coredata_codegen.sh b/Passepartout/App/Scripts/copy_coredata_codegen.sh deleted file mode 100755 index b9e2130c..00000000 --- a/Passepartout/App/Scripts/copy_coredata_codegen.sh +++ /dev/null @@ -1,9 +0,0 @@ -# Type a script or drag a script file from your workspace to insert its path. -CD_PROVIDERS_DIR="$PROJECT_TEMP_DIR/../PassepartoutCore.build/Debug-iphonesimulator/PassepartoutProviders.build/DerivedSources/CoreDataGenerated/Providers" -CD_CORE_DIR="$PROJECT_TEMP_DIR/../PassepartoutCore.build/Debug-iphonesimulator/PassepartoutCore.build/DerivedSources/CoreDataGenerated/Core" -if [ -d "$CD_PROVIDERS_DIR" ]; then - cp "$CD_PROVIDERS_DIR"/* "$PROJECT_DIR/PassepartoutCore/Sources/PassepartoutProviders/DataModels" -fi -if [ -d "$CD_CORE_DIR" ]; then - cp "$CD_CORE_DIR"/* "$PROJECT_DIR/PassepartoutCore/Sources/PassepartoutCore/DataModels" -fi diff --git a/Passepartout/App/Settings.bundle/Root.plist b/Passepartout/App/Settings.bundle/Root.plist deleted file mode 100644 index c2ab90b8..00000000 --- a/Passepartout/App/Settings.bundle/Root.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - StringsTable - Root - PreferenceSpecifiers - - - diff --git a/Passepartout/App/Settings.bundle/en.lproj/Root.strings b/Passepartout/App/Settings.bundle/en.lproj/Root.strings deleted file mode 100644 index 8cd87b9d..00000000 Binary files a/Passepartout/App/Settings.bundle/en.lproj/Root.strings and /dev/null differ diff --git a/Passepartout/App/Views/AboutView.swift b/Passepartout/App/Views/AboutView.swift deleted file mode 100644 index 8dea171d..00000000 --- a/Passepartout/App/Views/AboutView.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// AboutView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/7/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -struct AboutView: View { - private let versionString = Constants.Global.appVersionString - - private let redditURL = Constants.URLs.subreddit - - private let shareMessage = L10n.Global.Messages.share - - private let readmeURL = Constants.URLs.readme - - private let changelogURL = Constants.URLs.changelog - - private let homeURL = Constants.URLs.website - - private let faqURL = Constants.URLs.faq - - private let disclaimerURL = Constants.URLs.disclaimer - - private let privacyURL = Constants.URLs.privacyPolicy - - var body: some View { - List { - infoSection - supportSection - webSection - githubSection - }.navigationTitle(L10n.About.title) - .themeSecondaryView() - } -} - -// MARK: - - -private extension AboutView { - var infoSection: some View { - Section { - NavigationLink { - VersionView() - } label: { - Text(L10n.Version.title) - .withTrailingText(versionString) - } - NavigationLink(L10n.Credits.title) { - CreditsView() - } - } - } - - var supportSection: some View { - Section { - Button(L10n.About.Items.JoinCommunity.caption) { - URL.open(redditURL) - } - Button(L10n.About.Items.ShareTwitter.caption, action: shareOnTwitter) - Button(L10n.About.Items.WriteReview.caption, action: submitReview) - } header: { - Text(L10n.Menu.All.Support.title) - } - } - - var webSection: some View { - Section { - Button(L10n.About.Items.Website.caption) { - URL.open(homeURL) - } - Button(Unlocalized.About.faq) { - URL.open(faqURL) - } - Button(L10n.About.Items.Disclaimer.caption) { - URL.open(disclaimerURL) - } - Button(L10n.About.Items.PrivacyPolicy.caption) { - URL.open(privacyURL) - } - } header: { - Text(L10n.About.Sections.Web.header) - } - } - - var githubSection: some View { - Section { - Button(Unlocalized.About.readme) { - URL.open(readmeURL) - } - Button(Unlocalized.About.changelog) { - URL.open(changelogURL) - } - } header: { - Text(Unlocalized.About.github) - } - } -} - -// MARK: - - -private extension AboutView { - func shareOnTwitter() { - let url = Unlocalized.Social.twitterIntent(withMessage: shareMessage) - URL.open(url) - } - - func submitReview() { - let reviewURL = Reviewer.urlForReview(withAppId: Constants.App.appStoreId) - URL.open(reviewURL) - } -} diff --git a/Passepartout/App/Views/AccountView.swift b/Passepartout/App/Views/AccountView.swift deleted file mode 100644 index 11ea2224..00000000 --- a/Passepartout/App/Views/AccountView.swift +++ /dev/null @@ -1,157 +0,0 @@ -// -// AccountView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/11/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct AccountView: View { - enum Field { - case username - - case password - - case seed - } - - @ObservedObject private var providerManager: ProviderManager - - private let providerName: ProviderName? - - private let vpnProtocol: VPNProtocolType - - @Binding private var account: Profile.Account - - private let saveAnyway: Bool - - private let onSave: (() -> Void)? - - @State private var liveAccount = Profile.Account() - - @FocusState private var focusedField: Field? - - init( - providerName: ProviderName?, - vpnProtocol: VPNProtocolType, - account: Binding, - saveAnyway: Bool = false, - onSave: (() -> Void)? = nil - ) { - providerManager = .shared - self.providerName = providerName - self.vpnProtocol = vpnProtocol - _account = account - self.saveAnyway = saveAnyway - self.onSave = onSave - } - - var body: some View { - List { -// TODO: interactive, re-enable after fixing -// Section { -// // TODO: interactive, l10n -// themeTextPicker(L10n.Global.Strings.authentication, selection: $liveAccount.authenticationMethod ?? .persistent, values: [ -// .persistent, -// .interactive -//// .totp // TODO: interactive, support OTP-based authentication -// ], description: \.localizedDescription) -// } - Section { - TextField(usernamePlaceholder ?? L10n.Account.Items.Username.placeholder, text: $liveAccount.username) - .textContentType(.username) - .keyboardType(.emailAddress) - .focused($focusedField, equals: .username) - .themeRawTextStyle() - .withLeadingText(L10n.Account.Items.Username.caption) - - switch liveAccount.authenticationMethod { - case nil, .persistent, .interactive: - if liveAccount.authenticationMethod == .interactive { - EmptyView() - } else { - themeSecureField(L10n.Account.Items.Password.placeholder, text: $liveAccount.password) - .focused($focusedField, equals: .password) - .withLeadingText(L10n.Account.Items.Password.caption) - } - - case .totp: - // TODO: interactive, scan QR code - themeSecureField(L10n.Account.Items.Password.placeholder, text: $liveAccount.password, contentType: .oneTimeCode) - .focused($focusedField, equals: .seed) - .withLeadingText(L10n.Account.Items.Seed.caption) - } - } footer: { - metadata?.localizedDescription(optionalStyle: .guidance).map { - Text($0) - } - } - if vpnProtocol == .openVPN { - metadata?.openVPNGuidanceURL.map { guidanceURL in - Section { - Button(L10n.Account.Items.OpenGuide.caption) { - openGuidanceURL(guidanceURL) - } - } - } - } - }.toolbar { - CopySavingButton( - original: $account, - copy: $liveAccount, - mapping: \.stripped, - label: themeSaveButtonLabel, - saveAnyway: saveAnyway, - onSave: onSave - ) - }.onAppear { - focusedField = .username - }.navigationTitle(L10n.Account.title) - } -} - -// MARK: - - -private extension AccountView { - var usernamePlaceholder: String? { - guard let name = providerName else { - return nil - } - return providerManager.defaultUsername(name, vpnProtocol: vpnProtocol) - } - - var metadata: ProviderMetadata? { - guard let name = providerName else { - return nil - } - return providerManager.provider(withName: name) - } -} - -// MARK: - - -private extension AccountView { - func openGuidanceURL(_ url: URL) { - URL.open(url) - } -} diff --git a/Passepartout/App/Views/AddHostView+Name.swift b/Passepartout/App/Views/AddHostView+Name.swift deleted file mode 100644 index 5c5d67e1..00000000 --- a/Passepartout/App/Views/AddHostView+Name.swift +++ /dev/null @@ -1,222 +0,0 @@ -// -// AddHostView+Name.swift -// Passepartout -// -// Created by Davide De Rosa on 3/18/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI -import TunnelKitOpenVPN -import TunnelKitWireGuard - -extension AddHostView { - struct NameView: View { - @ObservedObject private var profileManager: ProfileManager - - private let url: URL - - private let deletingURLOnSuccess: Bool - - private let bindings: AddProfileView.Bindings - - @State private var viewModel = ViewModel() - - @State private var isEnteringCredentials = false - - @FocusState private var focusedField: AddProfileView.Field? - - init( - url: URL, - deletingURLOnSuccess: Bool, - bindings: AddProfileView.Bindings - ) { - profileManager = .shared - self.url = url - self.deletingURLOnSuccess = deletingURLOnSuccess - self.bindings = bindings - } - - var body: some View { - ZStack { - hiddenAccountLink - List { - mainView - }.themeAnimation(on: viewModel) - }.toolbar { - themeCloseItem(isPresented: bindings.$isPresented) - ToolbarItem(placement: .primaryAction) { - Button(nextString) { - if !viewModel.processedProfile.isPlaceholder { - saveProfile() - } else { - processProfile(replacingExisting: false) - } - } - } - }.alert( - L10n.AddProfile.Shared.title, - isPresented: $viewModel.isAskingOverwrite, - actions: alertOverwriteActions, - message: alertOverwriteMessage - ).onChange(of: viewModel.requiresPassphrase) { - if $0 { - focusedField = .passphrase - } - }.onAppear(perform: requestResourcePermissions) - .onDisappear(perform: dropResourcePermissions) - .navigationTitle(L10n.AddProfile.Shared.title) - .themeSecondaryView() - } - } -} - -// MARK: - - -private extension AddHostView.NameView { - - @ViewBuilder - var mainView: some View { - AddProfileView.ProfileNameSection( - profileName: $viewModel.profileName, - focusedField: $focusedField, - errorMessage: viewModel.errorMessage - ) { - processProfile(replacingExisting: false) - }.onAppear { - viewModel.presetName(withURL: url) - }.disabled(isComplete) - - if !isComplete { - if viewModel.requiresPassphrase { - encryptionSection - } - let headers = profileManager.headers.sorted() - if !headers.isEmpty { - AddProfileView.ExistingProfilesSection( - headers: headers, - profileName: $viewModel.profileName - ) - } - } else { - completeSection - } - } - - var encryptionSection: some View { - Section { - SecureField(L10n.AddProfile.Host.Sections.Encryption.footer, text: $viewModel.encryptionPassphrase) { - processProfile(replacingExisting: false) - }.focused($focusedField, equals: .passphrase) - } header: { - Text(L10n.Global.Strings.encryption) - } - } - - var completeSection: some View { - Section { - Text(Unlocalized.Network.url) - .withTrailingText(url.lastPathComponent) - viewModel.processedProfile.vpnProtocols.first.map { - Text(L10n.Global.Strings.protocol) - .withTrailingText($0.description) - } - } header: { - Text(L10n.AddProfile.Shared.title) - } footer: { - themeErrorMessage(viewModel.errorMessage) - } - } - - var hiddenAccountLink: some View { - NavigationLink("", isActive: $isEnteringCredentials) { - AddProfileView.AccountWrapperView( - profile: $viewModel.processedProfile, - bindings: bindings - ) - } - } - - var nextString: String { - if !viewModel.processedProfile.isPlaceholder { - return viewModel.processedProfile.requiresCredentials ? L10n.Global.Strings.next : L10n.Global.Strings.save - } else { - return L10n.Global.Strings.next - } - } - - @ViewBuilder - func alertOverwriteActions() -> some View { - Button(role: .destructive) { - processProfile(replacingExisting: true) - } label: { - Text(L10n.Global.Strings.ok) - } - Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.cancel) - } - } - - func alertOverwriteMessage() -> some View { - Text(L10n.AddProfile.Shared.Alerts.Overwrite.message) - } - - var isComplete: Bool { - !viewModel.processedProfile.isPlaceholder - } -} - -// MARK: - - -private extension AddHostView.NameView { - func requestResourcePermissions() { - _ = url.startAccessingSecurityScopedResource() - } - - func dropResourcePermissions() { - url.stopAccessingSecurityScopedResource() - } - - func processProfile(replacingExisting: Bool) { - viewModel.processURL( - url, - with: profileManager, - replacingExisting: replacingExisting, - deletingURLOnSuccess: deletingURLOnSuccess - ) - } - - func saveProfile() { - let result = viewModel.addProcessedProfile(to: profileManager) - guard result else { - return - } - - let profile = viewModel.processedProfile - if profile.requiresCredentials { - isEnteringCredentials = true - } else { - bindings.isPresented = false - profileManager.didCreateProfile.send(profile) - } - } -} diff --git a/Passepartout/App/Views/AddHostViewModel.swift b/Passepartout/App/Views/AddHostViewModel.swift deleted file mode 100644 index d78034c4..00000000 --- a/Passepartout/App/Views/AddHostViewModel.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// AddHostViewModel.swift -// Passepartout -// -// Created by Davide De Rosa on 2/14/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary -import TunnelKitOpenVPN -import TunnelKitWireGuard - -extension AddHostView { - struct ViewModel: Equatable { - private var isNamePreset = false - - var profileName = "" - - private(set) var requiresPassphrase = false - - var encryptionPassphrase = "" - - var processedProfile: Profile = .placeholder - - private(set) var errorMessage: String? - - var isAskingOverwrite = false - - mutating func presetName(withURL url: URL) { - guard !isNamePreset else { - return - } - isNamePreset = true - profileName = url.filename - } - - @MainActor - mutating func processURL( - _ url: URL, - with profileManager: ProfileManager, - replacingExisting: Bool, - deletingURLOnSuccess: Bool - ) { - profileName = profileName.stripped - guard !profileName.isEmpty else { - return - } - - if !replacingExisting { - guard !profileManager.isExistingProfile(withName: profileName) else { - isAskingOverwrite = true - return - } - } - - errorMessage = nil - do { - let profile = try profileManager.profile( - withHeader: .init(name: profileName), - fromURL: url, - passphrase: encryptionPassphrase - ) - processedProfile = profile - - if deletingURLOnSuccess { - try? FileManager.default.removeItem(at: url) - } - } catch { - if case Passepartout.ProfileError.decryptionFailure = error { - requiresPassphrase = true - } else { - requiresPassphrase = false - } - setErrorMessage(for: error) - } - } - - @MainActor - mutating func addProcessedProfile(to profileManager: ProfileManager) -> Bool { - guard !processedProfile.isPlaceholder else { - assertionFailure("Saving profile without processing first?") - return false - } - errorMessage = nil - profileManager.saveProfile(processedProfile, isActive: nil) - return true - } - - private mutating func setErrorMessage(for error: Error) { - setErrorMessage(AppError(error).localizedDescription) - } - - private mutating func setErrorMessage(_ message: String) { - errorMessage = message.withTrailingDot - } - } -} diff --git a/Passepartout/App/Views/AddProfileMenu.swift b/Passepartout/App/Views/AddProfileMenu.swift deleted file mode 100644 index 7273844e..00000000 --- a/Passepartout/App/Views/AddProfileMenu.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// AddProfileMenu.swift -// Passepartout -// -// Created by Davide De Rosa on 4/18/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct AddProfileMenu: View { - enum ModalType: Identifiable { - case addProvider - - case addHost(URL, Bool) - - // XXX: alert ids - var id: Int { - switch self { - case .addProvider: return 1 - - case .addHost: return 2 - } - } - } - - @Binding private var modalType: ModalType? - - @Binding private var isHostFileImporterPresented: Bool - - init(modalType: Binding, isHostFileImporterPresented: Binding) { - _modalType = modalType - _isHostFileImporterPresented = isHostFileImporterPresented - } - - var body: some View { - Menu { - Button { - modalType = .addProvider - } label: { - Label(L10n.Global.Strings.provider, systemImage: themeProviderImage) - } - Button(action: presentHostFileImporter) { - Label(L10n.Menu.Contextual.AddProfile.fromFiles, systemImage: themeHostFilesImage) - } -// Button { -// // TODO: add profile from text -// } label: { -// Label(L10n.Organizer.Menus.AddProfile.fromText, systemImage: themeHostTextImage) -// } - if let urls = importedURLs, !urls.isEmpty { - Divider() - ForEach(urls, id: \.absoluteString, content: importedURLRow) - } - } label: { - themeAddMenuImage.asSystemImage - }.sheet(item: $modalType, content: presentedModal) - } -} - -// MARK: - - -private extension AddProfileMenu { - - @ViewBuilder - func presentedModal(_ modalType: ModalType) -> some View { - switch modalType { - case .addProvider: - NavigationView { - AddProviderView( - bindings: .init( - isPresented: isModalPresented - ) - ) - }.themeGlobal() - - case .addHost(let url, let deletingURLOnSuccess): - NavigationView { - AddHostView.NameView( - url: url, - deletingURLOnSuccess: deletingURLOnSuccess, - bindings: .init( - isPresented: isModalPresented - ) - ) - }.themeGlobal() - } - } - - var isModalPresented: Binding { - .init { - modalType != nil - } set: { - if !$0 { - modalType = nil - } - } - } - - func importedURLRow(_ url: URL) -> some View { - Button(L10n.Menu.Contextual.AddProfile.imported(url.lastPathComponent)) { - presentAddHost(withURL: url, deletingURLOnSuccess: true) - } - } - - var importedURLs: [URL]? { - do { - let url = FileManager.default.userURL(for: .documentDirectory, appending: nil) - let list = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) - return list.filter { - VPNProtocolType.knownFileExtensions.contains($0.pathExtension) - } - } catch { - return nil - } - } -} - -// MARK: - - -private extension AddProfileMenu { - func presentAddProvider() { - modalType = .addProvider - } - - func presentAddHost(withURL url: URL, deletingURLOnSuccess: Bool) { - modalType = .addHost(url, deletingURLOnSuccess) - } - - func presentHostFileImporter() { - - // XXX: iOS bug, hack around crappy bug when dismissing by swiping down - // - // https://stackoverflow.com/questions/66965471/swiftui-fileimporter-modifier-not-updating-binding-when-dismissed-by-tapping - isHostFileImporterPresented = false - Task { @MainActor in - await Task.maybeWait(forMilliseconds: Constants.Delays.xxxPresentFileImporter) - isHostFileImporterPresented = true - } -// isHostFileImporterPresented = true -// -// // use this to test hardcoded bundle file -// let url = Bundle.main.url(forResource: "pia", withExtension: "ovpn")! -// importedProfileName = "pia.ovpn" -// modalType = .addHost(url, false) - } -} diff --git a/Passepartout/App/Views/AddProfileView.swift b/Passepartout/App/Views/AddProfileView.swift deleted file mode 100644 index 31228d6d..00000000 --- a/Passepartout/App/Views/AddProfileView.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// AddProfileView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -enum AddProfileView { - enum Field { - case name - - case passphrase - } - - struct Bindings { - @Binding var isPresented: Bool - } - - struct ProfileNameSection: View { - @Binding var profileName: String - - @FocusState.Binding var focusedField: Field? - - let errorMessage: String? - - let onCommit: () -> Void - - var body: some View { - Section { - TextField(L10n.Global.Placeholders.profileName, text: $profileName, onCommit: onCommit) - .focused($focusedField, equals: .name) - .themeValidProfileName() - } header: { - Text(L10n.Global.Strings.name) - } footer: { - themeErrorMessage(errorMessage) - }.onAppear { - focusedField = .name - } - } - } - - struct ExistingProfilesSection: View { - let headers: [Profile.Header] - - @Binding var profileName: String - - var body: some View { - Section { - ForEach(headers, content: existingProfileButton) - } header: { - Text(L10n.AddProfile.Shared.Views.Existing.header) - } - } - - private func existingProfileButton(_ header: Profile.Header) -> some View { - Button(header.name) { - profileName = header.name - }.themeLongTextStyle() - } - } - - struct AccountWrapperView: View { - @ObservedObject private var profileManager: ProfileManager - - @Binding private var profile: Profile - - private let bindings: AddProfileView.Bindings - - @State private var account = Profile.Account() - - init( - profile: Binding, - bindings: AddProfileView.Bindings - ) { - profileManager = .shared - _profile = profile - self.bindings = bindings - } - - var body: some View { - AccountView( - providerName: profile.header.providerName, - vpnProtocol: profile.currentVPNProtocol, - account: $account, - saveAnyway: true, - onSave: { - bindings.isPresented = false - } - ).navigationBarBackButtonHidden(true) - .onDisappear(perform: saveAccount) - } - - private func saveAccount() { - profile.account = account - profileManager.saveProfile(profile, isActive: nil) - profileManager.didCreateProfile.send(profile) - } - } -} diff --git a/Passepartout/App/Views/AddProviderView+Name.swift b/Passepartout/App/Views/AddProviderView+Name.swift deleted file mode 100644 index 6520fd6f..00000000 --- a/Passepartout/App/Views/AddProviderView+Name.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// AddProviderView+Name.swift -// Passepartout -// -// Created by Davide De Rosa on 3/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension AddProviderView { - struct NameView: View { - @ObservedObject private var profileManager: ProfileManager - - @Binding private var profile: Profile - - private let providerMetadata: ProviderMetadata - - private let bindings: AddProfileView.Bindings - - @State private var viewModel = ViewModel() - - @State private var isEnteringCredentials = false - - @FocusState private var focusedField: AddProfileView.Field? - - init( - profile: Binding, - providerMetadata: ProviderMetadata, - bindings: AddProfileView.Bindings - ) { - profileManager = .shared - _profile = profile - self.providerMetadata = providerMetadata - self.bindings = bindings - } - - var body: some View { - ZStack { - hiddenAccountLink - List { - AddProfileView.ProfileNameSection( - profileName: $viewModel.profileName, - focusedField: $focusedField, - errorMessage: viewModel.errorMessage - ) { - saveProfile(replacingExisting: false) - }.onAppear { - viewModel.presetName(withMetadata: providerMetadata) - } - let headers = profileManager.headers.sorted() - if !headers.isEmpty { - AddProfileView.ExistingProfilesSection( - headers: headers, - profileName: $viewModel.profileName - ) - } - } - }.toolbar { - Button { - saveProfile(replacingExisting: false) - } label: { - themeSaveButtonLabel() - } - }.alert( - L10n.AddProfile.Shared.title, - isPresented: $viewModel.isAskingOverwrite, - actions: alertOverwriteActions, - message: alertOverwriteMessage - ).navigationTitle(providerMetadata.fullName) - } - } -} - -// MARK: - - -private extension AddProviderView.NameView { - var hiddenAccountLink: some View { - NavigationLink("", isActive: $isEnteringCredentials) { - AddProfileView.AccountWrapperView( - profile: $profile, - bindings: bindings - ) - } - } - - @ViewBuilder - func alertOverwriteActions() -> some View { - Button(role: .destructive) { - saveProfile(replacingExisting: true) - } label: { - Text(L10n.Global.Strings.ok) - } - Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.cancel) - } - } - - func alertOverwriteMessage() -> some View { - Text(L10n.AddProfile.Shared.Alerts.Overwrite.message) - } -} - -// MARK: - - -private extension AddProviderView.NameView { - func saveProfile(replacingExisting: Bool) { - let addedProfile = viewModel.addProfile( - profile, - to: profileManager, - replacingExisting: replacingExisting - ) - guard let addedProfile = addedProfile else { - return - } - profile = addedProfile - - if profile.requiresCredentials { - isEnteringCredentials = true - } else { - bindings.isPresented = false - profileManager.didCreateProfile.send(profile) - } - } -} diff --git a/Passepartout/App/Views/AddProviderView.swift b/Passepartout/App/Views/AddProviderView.swift deleted file mode 100644 index f0b88d40..00000000 --- a/Passepartout/App/Views/AddProviderView.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// AddProviderView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/10/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct AddProviderView: View { - @ObservedObject private var providerManager: ProviderManager - - @ObservedObject private var productManager: ProductManager - - private let bindings: AddProfileView.Bindings - - @StateObject private var viewModel = ViewModel() - - init(bindings: AddProfileView.Bindings) { - providerManager = .shared - productManager = .shared - self.bindings = bindings - } - - var body: some View { - ZStack { - ForEach(providers, id: \.navigationId, content: hiddenProviderLink) - ScrollViewReader { scrollProxy in - List { - mainSection - if !providers.isEmpty { - providersSection - } - }.onAppear { - viewModel.updateIndex(providerManager) - }.onChange(of: viewModel.errorMessage) { - onErrorMessage($0, scrollProxy) - }.themeAnimation(on: providers) - } - }.toolbar { - themeCloseItem(isPresented: bindings.$isPresented) - }.sheet(isPresented: $viewModel.isPaywallPresented) { - NavigationView { - PaywallView(isPresented: $viewModel.isPaywallPresented, feature: .allProviders) - }.themeGlobal() - }.navigationTitle(L10n.AddProfile.Shared.title) - .themeSecondaryView() - } -} - -// MARK: - - -private extension AddProviderView { - var mainSection: some View { - Section { - let protos = availableVPNProtocols - if !protos.isEmpty { - themeTextPicker( - L10n.Global.Strings.protocol, - selection: $viewModel.selectedVPNProtocol, - values: protos, - description: \.description - ) - } - updateListButton - } footer: { - Text(L10n.AddProfile.Provider.Sections.Vpn.footer) - } - } - - var providersSection: some View { - Section { - ForEach(providers, content: providerRow) - } footer: { - themeErrorMessage(viewModel.errorMessage) - }.disabled(viewModel.isFetchingAnyProvider) - } - - func providerRow(_ metadata: ProviderMetadata) -> some View { - Button { - presentOrPurchaseProvider(metadata) - } label: { - Label(metadata.fullName, image: themeAssetsProviderImage(metadata.name)) - }.withTrailingProgress(when: viewModel.isFetchingProvider(metadata.name)) - } - - func hiddenProviderLink(_ metadata: ProviderMetadata) -> some View { - NavigationLink("", tag: metadata, selection: $viewModel.selectedProvider) { - NameView( - profile: $viewModel.pendingProfile, - providerMetadata: metadata, - bindings: bindings - ) - } - } - - var updateListButton: some View { - Button(L10n.AddProfile.Provider.Items.updateList) { - viewModel.updateIndex(providerManager) - }.withTrailingProgress(when: viewModel.isUpdatingIndex) - .disabled(viewModel.isUpdatingIndex) - } - - var providers: [ProviderMetadata] { - providerManager.allProviders() - .filter { - $0.supportedVPNProtocols.contains(viewModel.selectedVPNProtocol) - }.sorted() - } - - var availableVPNProtocols: [VPNProtocolType] { - var protos: Set = [] - providers.forEach { - $0.supportedVPNProtocols.forEach { - protos.insert($0) - } - } - return protos.sorted() - } -} - -private extension ProviderMetadata { - var navigationId: String { - "navigation.\(name)" - } -} - -// MARK: - - -private extension AddProviderView { - - // eligibility: select or purchase provider - func presentOrPurchaseProvider(_ metadata: ProviderMetadata) { - guard productManager.isEligible(forProvider: metadata.name) else { - viewModel.presentPaywall() - return - } - viewModel.selectProvider(metadata, providerManager) - } - - func onErrorMessage(_ message: String?, _ scrollProxy: ScrollViewProxy) { - guard message != nil else { - return - } - scrollToErrorMessage(scrollProxy) - } - - func scrollToErrorMessage(_ proxy: ScrollViewProxy) { - proxy.maybeScrollTo(providers.last?.id, animated: true) - } -} diff --git a/Passepartout/App/Views/AddProviderViewModel.swift b/Passepartout/App/Views/AddProviderViewModel.swift deleted file mode 100644 index 11b1db90..00000000 --- a/Passepartout/App/Views/AddProviderViewModel.swift +++ /dev/null @@ -1,195 +0,0 @@ -// -// AddProviderViewModel.swift -// Passepartout -// -// Created by Davide De Rosa on 3/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import PassepartoutLibrary - -extension AddProviderView { - - @MainActor - final class ViewModel: ObservableObject { - enum PendingOperation { - case index - - case provider(ProviderName) - } - - var isUpdatingIndex: Bool { - if case .index = pendingOperation { - return true - } - return false - } - - var isFetchingAnyProvider: Bool { - if case .provider = pendingOperation { - return true - } - return false - } - - func isFetchingProvider(_ name: ProviderName) -> Bool { - if case .provider(name) = pendingOperation { - return true - } - return false - } - - @Published var selectedVPNProtocol: VPNProtocolType = .openVPN - - @Published var selectedProvider: ProviderMetadata? - - @Published var pendingProfile: Profile = .placeholder - - @Published private(set) var pendingOperation: PendingOperation? - - @Published var isPaywallPresented = false - - @Published private(set) var errorMessage: String? - - func selectProvider(_ metadata: ProviderMetadata, _ providerManager: ProviderManager) { - errorMessage = nil - guard let server = providerManager.anyDefaultServer( - metadata.name, - vpnProtocol: selectedVPNProtocol - ) else { - selectProviderAfterFetchingInfrastructure(metadata, providerManager) - return - } - doSelectProvider(metadata, server) - } - - private func selectProviderAfterFetchingInfrastructure(_ metadata: ProviderMetadata, _ providerManager: ProviderManager) { - errorMessage = nil - pendingOperation = .provider(metadata.name) - Task { @MainActor in - do { - try await providerManager.fetchProviderPublisher( - withName: metadata.name, - vpnProtocol: pendingProfile.currentVPNProtocol, - priority: .remoteThenBundle - ).async() - - if let server = providerManager.anyDefaultServer( - metadata.name, - vpnProtocol: selectedVPNProtocol - ) { - doSelectProvider(metadata, server) - } else { - setErrorMessage(L10n.AddProfile.Provider.Errors.noDefaultServer) - } - } catch { - setErrorMessage(for: error) - } - pendingOperation = nil - } - } - - private func doSelectProvider(_ metadata: ProviderMetadata, _ server: ProviderServer) { - pendingProfile = Profile(metadata, server: server) - selectedProvider = metadata - } - - func updateIndex(_ providerManager: ProviderManager) { - errorMessage = nil - pendingOperation = .index - Task { @MainActor in - do { - try await providerManager.fetchProvidersIndexPublisher( - priority: .remoteThenBundle - ).async() - } catch { - setErrorMessage(for: error) - } - pendingOperation = nil - } - } - - func presentPaywall() { - isPaywallPresented = true - } - - private func setErrorMessage(for error: Error) { - setErrorMessage(AppError(error).localizedDescription) - } - - private func setErrorMessage(_ message: String) { - errorMessage = message.withTrailingDot - } - } -} - -extension AddProviderView.NameView { - struct ViewModel: Equatable { - private var isNamePreset = false - - var profileName = "" - - var isAskingOverwrite = false - - private(set) var errorMessage: String? - - mutating func presetName(withMetadata metadata: ProviderMetadata) { - guard !isNamePreset else { - return - } - isNamePreset = true - profileName = metadata.fullName - } - - @MainActor - mutating func addProfile( - _ profile: Profile, - to profileManager: ProfileManager, - replacingExisting: Bool - ) -> Profile? { - profileName = profileName.stripped - guard !profileName.isEmpty else { - return nil - } - - if !replacingExisting { - guard !profileManager.isExistingProfile(withName: profileName) else { - isAskingOverwrite = true - return nil - } - } - - errorMessage = nil - - let finalProfile = profile.renamed(to: profileName) - profileManager.saveProfile(finalProfile, isActive: nil) - return finalProfile - } - } -} - -extension Profile { - func renamed(to newName: String) -> Profile { - var profile = self - profile.header.name = newName - return profile - } -} diff --git a/Passepartout/App/Views/DebugLogView.swift b/Passepartout/App/Views/DebugLogView.swift deleted file mode 100644 index e7b268c1..00000000 --- a/Passepartout/App/Views/DebugLogView.swift +++ /dev/null @@ -1,174 +0,0 @@ -// -// DebugLogView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Combine -import PassepartoutLibrary -import SwiftUI - -struct DebugLogView: View { - private let title: String - - private let url: URL - - private let shareFilename: String - - private let timer: AnyPublisher - - @State private var logLines: [String] = [] - - @State private var isSharing = false - - private let maxBytes = UInt64(Constants.Log.maxBytes) - - private let appName = Constants.Global.appName - - private let appVersion = Constants.Global.appVersionString - - init(title: String, url: URL, filename: String, refreshInterval: TimeInterval?) { - self.title = title - self.url = url - shareFilename = filename - if let refreshInterval { - timer = Timer.TimerPublisher(interval: refreshInterval, runLoop: .main, mode: .common) - .autoconnect() - .eraseToAnyPublisher() - } else { - timer = Empty(outputType: Date.self, failureType: Never.self) - .eraseToAnyPublisher() - } - } - - var body: some View { - ScrollViewReader { scrollProxy in - ScrollView(showsIndicators: true) { - contentView - }.onAppear { - refreshLog(scrollingToLatestWith: scrollProxy) - } - } - #if !os(tvOS) - #if targetEnvironment(macCatalyst) - .toolbar { - Button(action: copyDebugLog) { - themeCopyImage.asSystemImage - }.disabled(logLines.isEmpty) - } - #else - .toolbar { - if !isSharing { - Button(action: shareDebugLog) { - themeShareImage.asSystemImage - }.disabled(logLines.isEmpty) - } else { - ProgressView() - } - } - .sheet(isPresented: $isSharing, content: sharingActivityView) - #endif - #endif - .edgesIgnoringSafeArea([.leading, .trailing]) - .onReceive(timer, perform: refreshLog) - .navigationTitle(title) - .themeDebugLogStyle() - } -} - -// MARK: - - -private extension DebugLogView { - var contentView: some View { - LazyVStack { - ForEach(logLines.indices, id: \.self) { - Text(logLines[$0]) - .frame(maxWidth: .infinity, alignment: .leading) - } - }// .padding() - // 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") - let data = DebugLog(content: raw) - .decoratedData(appName, appVersion) - - let path = NSTemporaryDirectory().appending(shareFilename) - let url = URL(fileURLWithPath: path) - do { - try data.write(to: url) - return [url] - } catch { - // highly unlikely to happen - assertionFailure("Unable to save temporary debug log file: \(error)") - return [] - } - } -} - -// MARK: - - -private extension DebugLogView { - func refreshLog(_: Date) { - refreshLog(scrollingToLatestWith: nil) - } - - func refreshLog(scrollingToLatestWith scrollProxy: ScrollViewProxy?) { - logLines = url.trailingLines(bytes: maxBytes) - if let scrollProxy = scrollProxy { - scrollToLatestUpdate(scrollProxy) - } - } - - #if !os(tvOS) - func shareDebugLog() { - guard !logLines.isEmpty else { - assertionFailure("Log is empty, why could it share?") - return - } - isSharing = true - } - - func copyDebugLog() { - guard !logLines.isEmpty else { - assertionFailure("Log is empty, why could it copy?") - return - } - let raw = logLines.joined(separator: "\n") - let content = DebugLog(content: raw) - .decoratedString(appName, appVersion) - - Utils.copyToPasteboard(content) - } - #endif - - func scrollToLatestUpdate(_ proxy: ScrollViewProxy) { - proxy.maybeScrollTo(logLines.count - 1, anchor: .bottomLeading) - } -} diff --git a/Passepartout/App/Views/DiagnosticsView+OpenVPN.swift b/Passepartout/App/Views/DiagnosticsView+OpenVPN.swift deleted file mode 100644 index 97eeb26e..00000000 --- a/Passepartout/App/Views/DiagnosticsView+OpenVPN.swift +++ /dev/null @@ -1,227 +0,0 @@ -// -// DiagnosticsView+OpenVPN.swift -// Passepartout -// -// Created by Davide De Rosa on 3/11/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI -import TunnelKitOpenVPN - -extension DiagnosticsView { - struct OpenVPNView: View { - enum AlertType: Int, Identifiable { - case emailNotConfigured - - var id: Int { - return rawValue - } - } - - @ObservedObject private var providerManager: ProviderManager - - @ObservedObject private var vpnManager: VPNManager - - @ObservedObject private var currentVPNState: ObservableVPNState - - @ObservedObject private var productManager: ProductManager - - private let providerName: ProviderName? - - @State private var isReportingIssue = false - - @State private var isAlertPresented = false - - @State private var alertType: AlertType? - - private let vpnProtocol: VPNProtocolType = .openVPN - - init(providerName: ProviderName?) { - providerManager = .shared - vpnManager = .shared - currentVPNState = .shared - productManager = .shared - self.providerName = providerName - } - - var body: some View { - List { - serverConfigurationSection - debugLogSection - - // eligibility: to report a connectivity issue - if isEligibleForFeedback { - issueReporterSection - } - }.sheet(isPresented: $isReportingIssue, content: reportIssueView) - .alert( - L10n.ReportIssue.Alert.title, - isPresented: $isAlertPresented, - presenting: alertType, - actions: alertActions, - message: alertMessage - ) - } - } -} - -// MARK: - - -private extension DiagnosticsView.OpenVPNView { - func alertActions(_ alertType: AlertType) -> some View { - Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.ok) - } - } - - func alertMessage(_ alertType: AlertType) -> some View { - switch alertType { - case .emailNotConfigured: - return Text(L10n.Global.Messages.emailNotConfigured) - } - } - - var serverConfigurationSection: some View { - Section { - let cfg = currentServerConfiguration - NavigationLink(L10n.Diagnostics.Items.ServerConfiguration.caption) { - cfg.map { - EndpointAdvancedView.OpenVPNView( - builder: .constant($0), - isReadonly: true, - isServerPushed: true - ).navigationTitle(L10n.Diagnostics.Items.ServerConfiguration.caption) - } - }.disabled(cfg == nil) - } - } - - var debugLogSection: some View { - Section { - DiagnosticsView.DebugLogGroup(appLogURL: appLogURL, tunnelLogURL: tunnelLogURL) - Toggle(L10n.Diagnostics.Items.MasksPrivateData.caption, isOn: $vpnManager.masksPrivateData) - } header: { - Text(L10n.DebugLog.title) - } footer: { - Text(L10n.Diagnostics.Sections.DebugLog.footer) - } - } - - var issueReporterSection: some View { - Section { - Button(L10n.Diagnostics.Items.ReportIssue.caption, action: presentReportIssue) - } - } - - func reportIssueView() -> some View { - ReportIssueView( - isPresented: $isReportingIssue, - vpnProtocol: vpnProtocol, - messageBody: messageBody, - logs: logs - ) - } - - var currentServerConfiguration: OpenVPN.ConfigurationBuilder? { - guard currentVPNState.vpnStatus == .connected else { - return nil - } - guard let cfg = vpnManager.serverConfiguration(forProtocol: vpnProtocol) as? OpenVPN.Configuration else { - return nil - } - // "withFallbacks: false" for view to hide nil options - return cfg.builder(withFallbacks: false) - } - - var messageBody: String { - var providerMetadata: ProviderMetadata? - var lastUpdate: Date? - if let name = providerName { - providerMetadata = providerManager.provider(withName: name) - lastUpdate = providerManager.lastUpdate(name, vpnProtocol: vpnProtocol) - } - return Unlocalized.Issues.body( - providerMetadata: providerMetadata, - lastUpdate: lastUpdate, - purchasedProductIdentifiers: productManager.purchasedProductIdentifiers - ) - } - - var logs: [MailComposerView.Attachment] { - var pairs: [(url: URL, filename: String)] = [] - if let appLogURL { - pairs.append((appLogURL, Unlocalized.Issues.Filenames.appLog)) - } - if let tunnelLogURL { - pairs.append((tunnelLogURL, Unlocalized.Issues.Filenames.tunnelLog)) - } - return pairs.map { - let logContent = $0.url.trailingContent(bytes: Unlocalized.Issues.maxLogBytes) - let attachment = DebugLog(content: logContent).decoratedData() - - return MailComposerView.Attachment( - data: attachment, - mimeType: Unlocalized.Issues.Filenames.mime, - fileName: $0.filename - ) - } - } - - var appLogURL: URL? { - Passepartout.shared.logger.logFile - } - - var tunnelLogURL: URL? { - vpnManager.debugLogURL(forProtocol: vpnProtocol) - } - - var isEligibleForFeedback: Bool { - productManager.isEligibleForFeedback() - } -} - -// MARK: - - -private extension DiagnosticsView.OpenVPNView { - func presentReportIssue() { - guard MailComposerView.canSendMail() else { - openReportIssueMailTo() - return - } - isReportingIssue = true - } - - func openReportIssueMailTo() { - let V = Unlocalized.Issues.self - guard let url = URL.mailto(to: V.recipient, subject: V.subject, body: messageBody) else { - return - } - guard URL.open(url) else { - alertType = .emailNotConfigured - isAlertPresented = true - return - } - } -} -#endif diff --git a/Passepartout/App/Views/DiagnosticsView+WireGuard.swift b/Passepartout/App/Views/DiagnosticsView+WireGuard.swift deleted file mode 100644 index 27704701..00000000 --- a/Passepartout/App/Views/DiagnosticsView+WireGuard.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// DiagnosticsView+WireGuard.swift -// Passepartout -// -// Created by Davide De Rosa on 3/11/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI -import TunnelKitWireGuard - -extension DiagnosticsView { - struct WireGuardView: View { - @ObservedObject private var vpnManager: VPNManager - - private let providerName: ProviderName? - - init(providerName: ProviderName?) { - vpnManager = .shared - self.providerName = providerName - } - - var body: some View { - List { - Section { - DebugLogGroup(appLogURL: appLogURL, tunnelLogURL: tunnelLogURL) - } header: { - Text(L10n.DebugLog.title) - } - } - } - } -} - -// MARK: - - -private extension DiagnosticsView.WireGuardView { - var appLogURL: URL? { - Passepartout.shared.logger.logFile - } - - var tunnelLogURL: URL? { - vpnManager.debugLogURL(forProtocol: .wireGuard) - } -} diff --git a/Passepartout/App/Views/DiagnosticsView.swift b/Passepartout/App/Views/DiagnosticsView.swift deleted file mode 100644 index f71d0f1a..00000000 --- a/Passepartout/App/Views/DiagnosticsView.swift +++ /dev/null @@ -1,128 +0,0 @@ -// -// DiagnosticsView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/20/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct DiagnosticsView: View { - let profile: Profile - - var body: some View { - Group { - if !profile.isPlaceholder { - #if !os(tvOS) - vpnView - #endif - } else { - genericView - } - }.navigationTitle(L10n.Diagnostics.title) - } -} - -private extension DiagnosticsView { - - #if !os(tvOS) - var vpnView: some View { - Group { - switch profile.currentVPNProtocol { - case .openVPN: - DiagnosticsView.OpenVPNView( - providerName: profile.header.providerName - ) - - case .wireGuard: - DiagnosticsView.WireGuardView( - providerName: profile.header.providerName - ) - } - } - } - #endif - - var genericView: some View { - List { - Section { - DebugLogGroup( - appLogURL: Passepartout.shared.logger.logFile, - tunnelLogURL: nil - ) - } header: { - Text(L10n.DebugLog.title) - } - } - } -} - -extension DiagnosticsView { - struct DebugLogGroup: View { - let appLogURL: URL? - - let tunnelLogURL: URL? - - private let refreshInterval = Constants.Log.refreshInterval - - var body: some View { - appLink - tunnelLink - } - } -} - -// MARK: - - -private extension DiagnosticsView.DebugLogGroup { - var appLink: some View { - navigationLink( - withTitle: L10n.Diagnostics.Items.AppLog.title, - url: appLogURL, - filename: Unlocalized.Issues.Filenames.appLog, - refreshInterval: nil - ) - } - - var tunnelLink: some View { - navigationLink( - withTitle: Unlocalized.VPN.vpn, - url: tunnelLogURL, - filename: Unlocalized.Issues.Filenames.tunnelLog, - refreshInterval: refreshInterval - ) - } - - func navigationLink(withTitle title: String, url: URL?, filename: String, refreshInterval: TimeInterval?) -> some View { - NavigationLink(title) { - url.map { - DebugLogView( - title: title, - url: $0, - filename: filename, - refreshInterval: refreshInterval - ) - } - } - .disabled(url == nil) - } -} diff --git a/Passepartout/App/Views/DonateView.swift b/Passepartout/App/Views/DonateView.swift deleted file mode 100644 index c7273178..00000000 --- a/Passepartout/App/Views/DonateView.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// DonateView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/8/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct DonateView: View { - enum AlertType: Identifiable { - case thankYou - - // XXX: alert ids - var id: Int { - switch self { - case .thankYou: return 1 - } - } - } - - @Environment(\.scenePhase) private var scenePhase - - @ObservedObject private var productManager: ProductManager - - @State private var isAlertPresented = false - - @State private var alertType: AlertType? - - @State private var pendingDonationIdentifier: String? - - init() { - productManager = .shared - } - - var body: some View { - List { - productsSection - .disabled(pendingDonationIdentifier != nil) - }.themeSecondaryView() - .navigationTitle(L10n.Donate.title) - .alert( - L10n.Donate.title, - isPresented: $isAlertPresented, - presenting: alertType, - actions: alertActions, - message: alertMessage - ) - - // reloading - .task { - await productManager.refreshProducts() - } - .onChange(of: scenePhase) { newValue in - if newValue == .active { - Task { - await productManager.refreshProducts() - } - } - } - .themeAnimation(on: productManager.isRefreshingProducts) - } -} - -// MARK: - - -private extension DonateView { - func alertActions(_ alertType: AlertType) -> some View { - switch alertType { - case .thankYou: - return Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.ok) - } - } - } - - func alertMessage(_ alertType: AlertType) -> some View { - switch alertType { - case .thankYou: - return Text(L10n.Donate.Alerts.Purchase.Success.message) - } - } - - var productsSection: some View { - Section { - if !productManager.isRefreshingProducts { - ForEach(productManager.donations, id: \.productIdentifier, content: productRow) - } else { - ProgressView() - } - } header: { - Text(L10n.Donate.Sections.OneTime.header) - } footer: { - Text(L10n.Donate.Sections.OneTime.footer) - } - } - - @ViewBuilder - func productRow(_ product: InAppProduct) -> some View { - HStack { - Button(product.localizedTitle) { - purchaseProduct(product) - } - Spacer() - if let pending = pendingDonationIdentifier, pending == product.productIdentifier { - ProgressView() - } else { - product.localizedPrice.map { - Text($0) - .themeSecondaryTextStyle() - } - } - } - } -} - -private extension ProductManager { - var donations: [InAppProduct] { - products.filter { product in - LocalProduct.allDonations.contains { - $0.matchesInAppProduct(product) - } - }.sorted { - $0.price.decimalValue < $1.price.decimalValue - } - } -} - -// MARK: - - -private extension DonateView { - func purchaseProduct(_ product: InAppProduct) { - pendingDonationIdentifier = product.productIdentifier - Task { - do { - let result = try await productManager.purchase(product) - handlePurchaseResult(result) - } catch { - handlePurchaseError(error) - } - } - } - - func handlePurchaseResult(_ result: InAppPurchaseResult) { - if case .done = result { - alertType = .thankYou - isAlertPresented = true - } else { - // cancelled - } - pendingDonationIdentifier = nil - } - - func handlePurchaseError(_ error: Error) { - ErrorHandler.shared.handle( - title: L10n.Donate.title, - message: L10n.Donate.Alerts.Purchase.Failure.message(AppError(error).localizedDescription) - ) - pendingDonationIdentifier = nil - } - -} diff --git a/Passepartout/App/Views/EndpointAdvancedView+OpenVPN.swift b/Passepartout/App/Views/EndpointAdvancedView+OpenVPN.swift deleted file mode 100644 index 00b65a8a..00000000 --- a/Passepartout/App/Views/EndpointAdvancedView+OpenVPN.swift +++ /dev/null @@ -1,436 +0,0 @@ -// -// EndpointAdvancedView+OpenVPN.swift -// Passepartout -// -// Created by Davide De Rosa on 3/8/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI -import TunnelKitOpenVPN - -extension EndpointAdvancedView { - struct OpenVPNView: View { - @Binding var builder: OpenVPN.ConfigurationBuilder - - let isReadonly: Bool - - let isServerPushed: Bool - - private let fallbackConfiguration = OpenVPN.ConfigurationBuilder(withFallbacks: true).build() - - var body: some View { - List { - let cfg = builder.build() - if !isServerPushed { - pullSection(configuration: cfg) - } - if builder.ipv4 != nil || builder.routes4 != nil { - ipv4Section - } - if builder.ipv6 != nil || builder.routes6 != nil { - ipv6Section - } - dnsSection(configuration: cfg) - proxySection(configuration: cfg) - if !isReadonly { - communicationEditableSection - compressionEditableSection - } else { - communicationSection(configuration: cfg) - compressionSection(configuration: cfg) - } - if !isServerPushed { - tlsSection - } - otherSection(configuration: cfg) - } - } - } -} - -// MARK: - - -private extension EndpointAdvancedView.OpenVPNView { - func pullSection(configuration: OpenVPN.Configuration) -> some View { - configuration.pullMask.map { mask in - Section { - ForEach(mask, id: \.self) { - Text($0.localizedDescription) - } - } header: { - Text(L10n.Endpoint.Advanced.Openvpn.Sections.Pull.header) - } - } - } - - var ipv4Section: some View { - Section { - if let settings = builder.ipv4 { - themeLongContentLinkDefault( - L10n.Global.Strings.address, - content: .constant(settings.localizedDescription(style: .address)) - ) - themeLongContentLinkDefault( - L10n.NetworkSettings.Gateway.title, - content: .constant(settings.localizedDescription(style: .defaultGateway)) - ) - } - builder.routes4.map { routes in - ForEach(routes, id: \.self) { route in - themeLongContentLinkDefault( - L10n.Endpoint.Advanced.Openvpn.Items.Route.caption, - content: .constant(route.localizedDescription) - ) - } - } - } header: { - Text(Unlocalized.Network.ipv4) - } - } - - var ipv6Section: some View { - Section { - if let settings = builder.ipv6 { - themeLongContentLinkDefault( - L10n.Global.Strings.address, - content: .constant(settings.localizedDescription(style: .address)) - ) - themeLongContentLinkDefault( - L10n.NetworkSettings.Gateway.title, - content: .constant(settings.localizedDescription(style: .defaultGateway)) - ) - } - builder.routes6.map { routes in - ForEach(routes, id: \.self) { route in - themeLongContentLinkDefault( - L10n.Endpoint.Advanced.Openvpn.Items.Route.caption, - content: .constant(route.localizedDescription) - ) - } - } - } header: { - Text(Unlocalized.Network.ipv6) - } - } - - func communicationSection(configuration: OpenVPN.Configuration) -> some View { - configuration.communicationSettings.map { settings in - Section { - settings.cipher.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.Cipher.caption) - .withTrailingText($0) - } - settings.digest.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.Digest.caption) - .withTrailingText($0) - } - if let xor = settings.xor { - themeLongContentLink( - Unlocalized.VPN.xor, - content: .constant(xor.longDescription), - withPreview: xor.shortDescription - ) - } else { - Text(Unlocalized.VPN.xor) - .withTrailingText(L10n.Global.Strings.disabled) - } - } header: { - Text(L10n.Endpoint.Advanced.Openvpn.Sections.Communication.header) - } - } - } - - var communicationEditableSection: some View { - Section { - themeTextPicker( - L10n.Endpoint.Advanced.Openvpn.Items.Cipher.caption, - selection: $builder.cipher ?? fallbackCipher, - values: OpenVPN.Cipher.available, - description: \.localizedDescription - ) - themeTextPicker( - L10n.Endpoint.Advanced.Openvpn.Items.Digest.caption, - selection: $builder.digest ?? fallbackDigest, - values: OpenVPN.Digest.available, - description: \.localizedDescription - ) - if let xor = builder.xorMethod { - themeLongContentLink( - Unlocalized.VPN.xor, - content: .constant(xor.localizedDescription(style: .long)), - withPreview: xor.localizedDescription(style: .short) - ) - } else { - Text(Unlocalized.VPN.xor) - .withTrailingText(L10n.Global.Strings.disabled) - } - } header: { - Text(L10n.Endpoint.Advanced.Openvpn.Sections.Communication.header) - } - } - - func compressionSection(configuration: OpenVPN.Configuration) -> some View { - configuration.compressionSettings.map { settings in - Section { - settings.framing.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.CompressionFraming.caption) - .withTrailingText($0) - } - settings.algorithm.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.CompressionAlgorithm.caption) - .withTrailingText($0) - } - } header: { - Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header) - } - } - } - - var compressionEditableSection: some View { - Section { - themeTextPicker( - L10n.Endpoint.Advanced.Openvpn.Items.CompressionFraming.caption, - selection: $builder.compressionFraming ?? fallbackCompressionFraming, - values: OpenVPN.CompressionFraming.available, - description: \.localizedDescription - ) - themeTextPicker( - L10n.Endpoint.Advanced.Openvpn.Items.CompressionAlgorithm.caption, - selection: $builder.compressionAlgorithm ?? fallbackCompressionAlgorithm, - values: OpenVPN.CompressionAlgorithm.available, - description: \.localizedDescription - ).disabled(builder.compressionFraming == .disabled) - } header: { - Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header) - } - } - - func dnsSection(configuration: OpenVPN.Configuration) -> some View { - configuration.dnsSettings.map { settings in - Section { - ForEach(settings.servers, id: \.self) { - Text(L10n.Global.Strings.address) - .withTrailingText($0, copyOnTap: true) - } - ForEach(settings.domains, id: \.self) { - Text(L10n.Global.Strings.domain) - .withTrailingText($0, copyOnTap: true) - } - } header: { - Text(Unlocalized.Network.dns) - } - } - } - - func proxySection(configuration: OpenVPN.Configuration) -> some View { - configuration.proxySettings.map { settings in - Section { - settings.proxy.map { - Text(L10n.Global.Strings.address) - .withTrailingText($0, copyOnTap: true) - } - settings.pac.map { - Text(Unlocalized.Network.proxyAutoConfiguration) - .withTrailingText($0, copyOnTap: true) - } - ForEach(settings.bypass, id: \.self) { - Text(L10n.NetworkSettings.Items.ProxyBypass.caption) - .withTrailingText($0, copyOnTap: true) - } - } header: { - Text(L10n.Global.Strings.proxy) - } - } - } - - var tlsSection: some View { - Section { - builder.ca.map { ca in - themeLongContentLink( - Unlocalized.VPN.certificateAuthority, - content: .constant(ca.pem) - ) - } - builder.clientCertificate.map { cert in - themeLongContentLink( - L10n.Endpoint.Advanced.Openvpn.Items.Client.caption, - content: .constant(cert.pem) - ) - } - builder.clientKey.map { key in - themeLongContentLink( - L10n.Endpoint.Advanced.Openvpn.Items.ClientKey.caption, - content: .constant(key.pem) - ) - } - builder.tlsWrap.map { wrap in - themeLongContentLink( - L10n.Endpoint.Advanced.Openvpn.Items.TlsWrapping.caption, - content: .constant(wrap.key.hexString), - withPreview: builder.localizedDescription(style: .tlsWrap) - ) - } - Text(L10n.Endpoint.Advanced.Openvpn.Items.Eku.caption) - .withTrailingText(builder.localizedDescription(style: .eku)) - } header: { - Text(Unlocalized.Network.tls) - } - } - - func otherSection(configuration: OpenVPN.Configuration) -> some View { - configuration.otherSettings.map { settings in - Section { - settings.keepAlive.map { - Text(L10n.Global.Strings.keepalive) - .withTrailingText($0) - } - settings.reneg.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.RenegotiationSeconds.caption) - .withTrailingText($0) - } - settings.randomizeEndpoint.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.RandomEndpoint.caption) - .withTrailingText($0) - } - settings.randomizeHostnames.map { - Text(L10n.Endpoint.Advanced.Openvpn.Items.RandomHostname.caption) - .withTrailingText($0) - } - } header: { - Text(L10n.Endpoint.Advanced.Openvpn.Sections.Other.header) - } - } - } -} - -private extension OpenVPN.Configuration { - struct CommunicationOptions { - let cipher: String? - - let digest: String? - - let xor: (shortDescription: String, longDescription: String)? - } - - struct CompressionOptions { - let framing: String? - - let algorithm: String? - } - - struct DNSOptions { - let servers: [String] - - let domains: [String] - } - - struct ProxyOptions { - let proxy: String? - - let pac: String? - - let bypass: [String] - } - - struct OtherOptions { - let keepAlive: String? - - let reneg: String? - - let randomizeEndpoint: String? - - let randomizeHostnames: String? - } - - var communicationSettings: CommunicationOptions? { - guard cipher != nil || digest != nil || xorMethod != nil else { - return nil - } - return .init( - cipher: cipher?.localizedDescription, - digest: digest?.localizedDescription, - xor: xorMethod.map { - ($0.localizedDescription(style: .short), $0.localizedDescription(style: .long)) - } - ) - } - - var compressionSettings: CompressionOptions? { - guard compressionFraming != nil || compressionAlgorithm != nil else { - return nil - } - return .init( - framing: compressionFraming?.localizedDescription, - algorithm: compressionAlgorithm?.localizedDescription - ) - } - - var dnsSettings: DNSOptions? { - guard !(dnsServers?.isEmpty ?? true) || !(searchDomains?.isEmpty ?? true) else { - return nil - } - return .init(servers: dnsServers ?? [], domains: searchDomains ?? []) - } - - var proxySettings: ProxyOptions? { - guard httpsProxy != nil || httpProxy != nil || - proxyAutoConfigurationURL != nil || !(proxyBypassDomains?.isEmpty ?? true) else { - return nil - } - return .init( - proxy: (httpsProxy ?? httpProxy)?.rawValue, - pac: proxyAutoConfigurationURL?.absoluteString, - bypass: proxyBypassDomains ?? [] - ) - } - - var otherSettings: OtherOptions? { - guard keepAliveInterval != nil || renegotiatesAfter != nil || - randomizeEndpoint != nil || randomizeHostnames != nil else { - return nil - } - return .init( - keepAlive: localizedDescription(optionalStyle: .keepAlive), - reneg: localizedDescription(optionalStyle: .renegotiatesAfter), - randomizeEndpoint: localizedDescription(optionalStyle: .randomizeEndpoint), - randomizeHostnames: localizedDescription(optionalStyle: .randomizeHostnames) - ) - } -} - -private extension EndpointAdvancedView.OpenVPNView { - var fallbackCipher: OpenVPN.Cipher { - fallbackConfiguration.cipher! - } - - var fallbackDigest: OpenVPN.Digest { - fallbackConfiguration.digest! - } - - var fallbackCompressionFraming: OpenVPN.CompressionFraming { - fallbackConfiguration.compressionFraming! - } - - var fallbackCompressionAlgorithm: OpenVPN.CompressionAlgorithm { - fallbackConfiguration.compressionAlgorithm! - } -} diff --git a/Passepartout/App/Views/EndpointAdvancedView+WireGuard.swift b/Passepartout/App/Views/EndpointAdvancedView+WireGuard.swift deleted file mode 100644 index 7f924cc1..00000000 --- a/Passepartout/App/Views/EndpointAdvancedView+WireGuard.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// EndpointAdvancedView+WireGuard.swift -// Passepartout -// -// Created by Davide De Rosa on 3/8/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI -import TunnelKitWireGuard - -extension EndpointAdvancedView { - struct WireGuardView: View { - @Binding var builder: WireGuard.ConfigurationBuilder - - let isReadonly: Bool - - var body: some View { - List { - let cfg = builder.build() - keySection - addressesSection - dnsSection(configuration: cfg) - mtuSection - } - } - } -} - -// MARK: - - -private extension EndpointAdvancedView.WireGuardView { - var keySection: some View { - Section { - themeLongContentLink(L10n.Global.Strings.privateKey, content: .constant(builder.privateKey)) - themeLongContentLink(L10n.Global.Strings.publicKey, content: .constant(builder.publicKey)) - } header: { - Text(L10n.Global.Strings.interface) - } - } - - var addressesSection: some View { - Section { - ForEach(builder.addresses, id: \.self, content: Text.init) - } header: { - Text(L10n.Global.Strings.addresses) - } - } - - func dnsSection(configuration: WireGuard.Configuration) -> some View { - configuration.dnsSettings.map { settings in - Section { - ForEach(settings.servers, id: \.self) { - Text(L10n.Global.Strings.address) - .withTrailingText($0) - } - ForEach(settings.domains, id: \.self) { - Text(L10n.Global.Strings.domain) - .withTrailingText($0) - } - } header: { - Text(Unlocalized.Network.dns) - } - } - } - - var mtuSection: some View { - builder.mtu.map { mtu in - Section { - Text(Unlocalized.Network.mtu) - .withTrailingText(Int(mtu).localizedDescription(style: .mtu)) - } - } - } -} - -private extension WireGuard.Configuration { - struct DNSOptions { - let servers: [String] - - let domains: [String] - } - - var dnsSettings: DNSOptions? { - guard !dnsServers.isEmpty || !dnsSearchDomains.isEmpty else { - return nil - } - return .init(servers: dnsServers, domains: dnsSearchDomains) - } -} diff --git a/Passepartout/App/Views/EndpointView+Add.swift b/Passepartout/App/Views/EndpointView+Add.swift deleted file mode 100644 index 3b7304f8..00000000 --- a/Passepartout/App/Views/EndpointView+Add.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// EndpointView+Add.swift -// Passepartout -// -// Created by Davide De Rosa on 7/22/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI -import TunnelKitCore - -extension EndpointView { - struct AddView: View { - @Environment(\.presentationMode) private var presentationMode - - private let title: String - - private let endpoint: Endpoint? - - private let onSave: ((Endpoint, Endpoint?) -> Void)? - - @State private var socketType: SocketType = .udp - - @State private var address = "" - - @State private var port = "" - - @State private var didAppear = false - - private let allSocketTypes: [SocketType] = [ - .udp, - .udp4, - .udp6, - .tcp, - .tcp4, - .tcp6 - ] - - init(_ title: String, endpoint: Endpoint? = nil, onSave: ((Endpoint, Endpoint?) -> Void)? = nil) { - self.title = title - self.endpoint = endpoint - self.onSave = onSave - } - - var body: some View { - List { - Section { - themeTextPicker( - L10n.Global.Strings.protocol, - selection: $socketType, - values: allSocketTypes, - description: \.rawValue - ) - TextField(L10n.Global.Strings.address, text: $address, onCommit: commitChanges) - .themeValidIPAddress(address) - TextField(L10n.Global.Strings.port, text: $port, onCommit: commitChanges) - .themeValidSocketPort(port) - } - }.onAppear { - guard !didAppear, let endpoint else { - return - } - socketType = endpoint.proto.socketType - address = endpoint.address - port = String(endpoint.proto.port) - didAppear = true - }.themeSecondaryView() - .navigationTitle(title) - .toolbar { - themeCloseItem(presentationMode: presentationMode) - ToolbarItem(placement: .primaryAction) { - Button(action: commitChanges, label: themeSaveButtonLabel) - } - } - } - } -} - -// MARK: - - -private extension EndpointView.AddView { -} - -// MARK: - - -private extension EndpointView.AddView { - func commitChanges() { - let endpointString = "\(address):\(socketType.rawValue):\(port)" - guard let newEndpoint = Endpoint(rawValue: endpointString) else { - return - } - onSave?(newEndpoint, endpoint) - presentationMode.wrappedValue.dismiss() - } -} -#endif diff --git a/Passepartout/App/Views/EndpointView+OpenVPN.swift b/Passepartout/App/Views/EndpointView+OpenVPN.swift deleted file mode 100644 index 384c6a24..00000000 --- a/Passepartout/App/Views/EndpointView+OpenVPN.swift +++ /dev/null @@ -1,398 +0,0 @@ -// -// EndpointView+OpenVPN.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI -import TunnelKitOpenVPN - -extension EndpointView { - struct OpenVPNView: View { - @ObservedObject private var providerManager: ProviderManager - - @ObservedObject private var currentProfile: ObservableProfile - - @Binding private var builder: OpenVPN.ConfigurationBuilder - - @State private var isFirstAppearance = true - - @State private var isAutomatic = false - - @State private var isExpanded: [String: Bool] = [:] - - @State private var isAdding = false - - @State private var editedEndpoint: Endpoint? - - init(currentProfile: ObservableProfile) { - let providerManager: ProviderManager = .shared - - self.providerManager = providerManager - self.currentProfile = currentProfile - - _builder = currentProfile.builderBinding(providerManager: providerManager) - } - - var body: some View { - ScrollViewReader { scrollProxy in - List { - mainSection - if isConfigurationReadonly { - groupedEndpointsSections - } else { - endpointsSection - } - advancedSection - }.onAppear { - isAutomatic = (currentProfile.value.customEndpoint == nil) - if let customEndpoint = currentProfile.value.customEndpoint { - isExpanded[customEndpoint.address] = true - } - scrollToCustomEndpoint(scrollProxy) - }.onChange(of: isAutomatic, perform: onToggleAutomatic) - .toolbar { - if !isConfigurationReadonly { - addButton - } - } - }.navigationTitle(L10n.Global.Strings.endpoint) - } - } -} - -// MARK: - - -private extension EndpointView.OpenVPNView { - var isConfigurationReadonly: Bool { - currentProfile.value.isProvider - } - - var mainSection: some View { - Section { - Toggle(L10n.Global.Strings.automatic, isOn: $isAutomatic.themeAnimation()) - } footer: { - themeErrorMessage(isManualEndpointRequired ? L10n.Endpoint.Errors.endpointRequired : nil) - } - } - - var advancedSection: some View { - Section { - let caption = L10n.Endpoint.Advanced.title - NavigationLink(caption) { - EndpointAdvancedView.OpenVPNView( - builder: $builder, - isReadonly: isConfigurationReadonly, - isServerPushed: false - ).navigationTitle(caption) - } - } - } - - var isManualEndpointRequired: Bool { - !isAutomatic && currentProfile.value.customEndpoint == nil - } -} - -// MARK: - - -private extension EndpointView.OpenVPNView { - func onToggleAutomatic(_ value: Bool) { - guard value else { - return - } - guard currentProfile.value.customEndpoint != nil else { - return - } - withAnimation { - currentProfile.value.customEndpoint = nil - isExpanded.removeAll() - } - } - - func scrollToCustomEndpoint(_ proxy: ScrollViewProxy) { - proxy.maybeScrollTo(currentProfile.value.customEndpoint?.id) - } -} - -// MARK: - Editable: linear - -private extension EndpointView.OpenVPNView { - var endpointsSection: some View { - Section { - ForEach(builder.remotes ?? []) { endpoint in - rowForEndpoint(endpoint) - .swipeActions(edge: .trailing, allowsFullSwipe: false) { - actions(forEndpoint: endpoint) - } - }.onMove(perform: moveEndpoints) - .disabled(isAutomatic) - } - } - - func rowForEndpoint(_ endpoint: Endpoint) -> some View { - Button { - withAnimation { - currentProfile.value.customEndpoint = endpoint - } - } label: { - labelForEndpoint(endpoint) - }.sheet(item: $editedEndpoint) { endpoint in - NavigationView { - EndpointView.AddView(L10n.Global.Strings.edit, endpoint: endpoint, onSave: commitEndpoint) - }.themeGlobal() - }.withTrailingCheckmark(when: currentProfile.value.customEndpoint == endpoint) - } - - func labelForEndpoint(_ endpoint: Endpoint) -> some View { - VStack { - Text(endpoint.address) - .frame(maxWidth: .infinity, alignment: .leading) - Text(endpoint.proto.rawValue) - .frame(maxWidth: .infinity, alignment: .leading) - } - } - - @ViewBuilder - func actions(forEndpoint endpoint: Endpoint) -> some View { - if !isConfigurationReadonly { - if (builder.remotes?.count ?? 0) > 1 { - removeButton(forEndpoint: endpoint) - } - editButton(forEndpoint: endpoint) - } - } - - var addButton: some View { - Button { - isAdding = true - } label: { - themeAddMenuImage.asSystemImage - }.sheet(isPresented: $isAdding) { - NavigationView { - EndpointView.AddView(L10n.Global.Strings.add, onSave: commitEndpoint) - }.themeGlobal() - } - } - - func removeButton(forEndpoint endpoint: Endpoint) -> some View { - Button(role: .destructive) { - deleteEndpoint(endpoint) - } label: { - Text(L10n.Global.Strings.delete) - }.themeDestructiveTintStyle() - } - - func editButton(forEndpoint endpoint: Endpoint) -> some View { - Button { - editedEndpoint = endpoint - } label: { - Text(L10n.Global.Strings.edit) - }.themePrimaryTintStyle() - } -} - -private extension EndpointView.OpenVPNView { - func commitEndpoint(_ newEndpoint: Endpoint, editedEndpoint: Endpoint?) { - withAnimation { - - // replace existing - if let editedEndpoint, - let editedIndex = builder.remotes?.firstIndex(where: { $0 == editedEndpoint }) { - - builder.remotes?[editedIndex] = newEndpoint - if currentProfile.value.customEndpoint == editedEndpoint { - currentProfile.value.customEndpoint = newEndpoint - } - } - // add new - else { - if builder.remotes != nil { - builder.remotes?.append(newEndpoint) - } else { - assertionFailure("Nil remotes, how did we get here?") - builder.remotes = [newEndpoint] - } - } - } - } - - func moveEndpoints(fromOffsets: IndexSet, toOffset: Int) { - builder.remotes?.move(fromOffsets: fromOffsets, toOffset: toOffset) - } - - func deleteEndpoint(_ endpoint: Endpoint) { - withAnimation { - builder.remotes?.removeAll { - $0 == endpoint - } - if currentProfile.value.customEndpoint == endpoint { - currentProfile.value.customEndpoint = nil - } - } - } -} - -// MARK: - Non-editable: group by address - -private extension EndpointView.OpenVPNView { - var groupedEndpointsSections: some View { - ForEach(endpointsByAddress, content: group(forEndpointsSection:)) - .disabled(isAutomatic) - } - - func group(forEndpointsSection section: EndpointsByAddress) -> some View { - Section { - DisclosureGroup(isExpanded: isExpandedBinding(address: section.address)) { - ForEach(section.endpoints, content: rowForEndpointProtocol) - } label: { - labelForEndpointAddress(section.address) - } - } - } - - func labelForEndpointAddress(_ address: String) -> some View { - Text(address) - } - - func rowForEndpointProtocol(_ endpoint: Endpoint) -> some View { - Button { - withAnimation { - currentProfile.value.customEndpoint = endpoint - } - } label: { - labelForEndpointProtocol(endpoint.proto) - }.withTrailingCheckmark(when: currentProfile.value.customEndpoint == endpoint) - } - - func labelForEndpointProtocol(_ proto: EndpointProtocol) -> some View { - Text(proto.rawValue) - } - - var endpointsByAddress: [EndpointsByAddress] { - guard let remotes = builder.remotes, !remotes.isEmpty else { - return [] - } - var uniqueAddresses: [String] = [] - remotes.forEach { - guard !uniqueAddresses.contains($0.address) else { - return - } - uniqueAddresses.append($0.address) - } - return uniqueAddresses.map { - EndpointsByAddress(address: $0, remotes: remotes) - } - } -} - -private struct EndpointsByAddress: Identifiable { - let address: String - - let endpoints: [Endpoint] - - init(address: String, remotes: [Endpoint]?) { - self.address = address - endpoints = remotes?.filter { - $0.address == address - }.sorted() ?? [] - } - - // MARK: Identifiable - - var id: String { - address - } -} - -// MARK: - Bindings - -private extension ObservableProfile { - - @MainActor - func builderBinding(providerManager: ProviderManager) -> Binding { - .init { - if self.value.isProvider { - guard let server = self.value.providerServer(providerManager) else { - assertionFailure("Server not found") - return .init() - } - guard let preset = self.value.providerPreset(server) else { - assertionFailure("Preset not found") - return .init() - } - guard let cfg = preset.openVPNConfiguration else { - assertionFailure("Preset \(preset.id) (\(preset.name)) has no OpenVPN configuration") - return .init() - } - var builder = cfg.builder(withFallbacks: true) - try? builder.setRemotes(from: preset, with: server, excludingHostname: false) - return builder - } else if let cfg = self.value.hostOpenVPNSettings?.configuration { - let builder = cfg.builder(withFallbacks: true) -// pp_log.debug("Loading OpenVPN configuration: \(builder)") - return builder - } - // fall back gracefully - return .init() - } set: { - if self.value.isProvider { - // readonly - } else { - pp_log.verbose("Saving OpenVPN configuration: \($0)") - self.value.hostOpenVPNSettings?.configuration = $0.build() - } - } - } -} - -private extension EndpointView.OpenVPNView { - func isExpandedBinding(address: String) -> Binding { - .init { - isExpanded[address] ?? false - } set: { - isExpanded[address] = $0 - } - } -} - -private extension Profile { - var customEndpoint: Endpoint? { - get { - if isProvider { - return providerCustomEndpoint - } else { - return hostOpenVPNSettings?.customEndpoint - } - } - set { - if isProvider { - providerCustomEndpoint = newValue - } else { - hostOpenVPNSettings?.customEndpoint = newValue - } - } - } -} -#endif diff --git a/Passepartout/App/Views/EndpointView+WireGuard.swift b/Passepartout/App/Views/EndpointView+WireGuard.swift deleted file mode 100644 index e8b7e25e..00000000 --- a/Passepartout/App/Views/EndpointView+WireGuard.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// EndpointView+WireGuard.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI -import TunnelKitWireGuard - -extension EndpointView { - struct WireGuardView: View { - @ObservedObject private var providerManager: ProviderManager - - @ObservedObject private var currentProfile: ObservableProfile - - let isReadonly: Bool - - @Binding private var builder: WireGuard.ConfigurationBuilder - -// var customPeer: Binding? = nil - - init(currentProfile: ObservableProfile, isReadonly: Bool) { - let providerManager: ProviderManager = .shared - - self.providerManager = providerManager - self.currentProfile = currentProfile - self.isReadonly = isReadonly - - _builder = currentProfile.builderBinding(providerManager: providerManager) - } - - var body: some View { - List { - peersSections - advancedSection - }.navigationTitle(L10n.Global.Strings.endpoint) - } - } -} - -// MARK: - - -private extension EndpointView.WireGuardView { - var peersSections: some View { - - // TODO: WireGuard, make peers editable -// if !isReadonly { - ForEach(0.. some View { - Section { - themeLongContentLink( - L10n.Global.Strings.publicKey, - content: .constant(builder.publicKey(ofPeer: peerIndex)) - ) - builder.preSharedKey(ofPeer: peerIndex).map { key in - themeLongContentLink( - L10n.Endpoint.Wireguard.Items.PresharedKey.caption, - content: .constant(key) - ) - } - builder.endpoint(ofPeer: peerIndex).map { - Text(L10n.Global.Strings.endpoint) - .withTrailingText($0) - } - ForEach(builder.allowedIPs(ofPeer: peerIndex), id: \.self) { - Text(L10n.Endpoint.Wireguard.Items.AllowedIp.caption) - .withTrailingText($0) - } - builder.localizedDescription(optionalStyle: .keepAlive(peerIndex: peerIndex)).map { - Text(L10n.Global.Strings.keepalive) - .withTrailingText($0) - } - } header: { - Text(L10n.Endpoint.Wireguard.Items.Peer.caption) - } - } - - var advancedSection: some View { - Section { - let caption = L10n.Endpoint.Advanced.title - NavigationLink(caption) { - EndpointAdvancedView.WireGuardView( - builder: $builder, - isReadonly: isReadonly - ).navigationTitle(caption) - } - } - } -} - -// MARK: - Bindings - -private extension ObservableProfile { - - @MainActor - func builderBinding(providerManager: ProviderManager) -> Binding { - .init { - if self.value.isProvider { - guard let server = self.value.providerServer(providerManager) else { - assertionFailure("Server not found") - return .init() - } - guard let preset = self.value.providerPreset(server) else { - assertionFailure("Preset not found") - return .init() - } - guard let cfg = preset.wireGuardConfiguration else { - assertionFailure("Preset \(preset.id) (\(preset.name)) has no WireGuard configuration") - return .init() - } - return cfg.builder() - } else if let cfg = self.value.hostWireGuardSettings?.configuration { - let builder = cfg.builder() -// pp_log.debug("Loading WireGuard configuration: \(builder)") - return builder - } - // fall back gracefully - return .init() - } set: { - if self.value.isProvider { - // readonly - } else { - pp_log.verbose("Saving WireGuard configuration: \($0)") - self.value.hostWireGuardSettings?.configuration = $0.build() - } - } - } -} -#endif diff --git a/Passepartout/App/Views/InteractiveConnectionView.swift b/Passepartout/App/Views/InteractiveConnectionView.swift deleted file mode 100644 index c8dcef09..00000000 --- a/Passepartout/App/Views/InteractiveConnectionView.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// InteractiveConnectionView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/15/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct InteractiveConnectionView: View { - @Environment(\.presentationMode) private var presentationMode - - @ObservedObject private var profileManager: ProfileManager - - @ObservedObject private var vpnManager: VPNManager - - private let profile: Profile - - @State private var password = "" - - init(profile: Profile) { - profileManager = .shared - vpnManager = .shared - self.profile = profile - } - - var body: some View { - List { - Section { - TextField(L10n.Account.Items.Username.placeholder, text: .constant(profile.account.username)) - .withLeadingText(L10n.Account.Items.Username.caption) - .disabled(true) - - themeSecureField(L10n.Account.Items.Password.placeholder, text: $password) - .withLeadingText(L10n.Account.Items.Password.caption) - } header: { - Text(L10n.Account.title) - } - }.toolbar { - themeCloseItem(presentationMode: presentationMode) - ToolbarItem(placement: .confirmationAction) { - Button(action: saveAccount) { - Text(L10n.Global.Strings.connect) - } - } - }.navigationTitle(profile.header.name) - } -} - -// MARK: - - -private extension InteractiveConnectionView { - func saveAccount() { - Task { - try? await vpnManager.connect(with: profile.id, newPassword: password) - } - presentationMode.wrappedValue.dismiss() - } -} diff --git a/Passepartout/App/Views/NetworkSettingsView.swift b/Passepartout/App/Views/NetworkSettingsView.swift deleted file mode 100644 index 9e0462a8..00000000 --- a/Passepartout/App/Views/NetworkSettingsView.swift +++ /dev/null @@ -1,299 +0,0 @@ -// -// NetworkSettingsView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct NetworkSettingsView: View { - @ObservedObject private var currentProfile: ObservableProfile - - @State private var settings = Profile.NetworkSettings() - - init(currentProfile: ObservableProfile) { - self.currentProfile = currentProfile - } - - var body: some View { - debugChanges() - return List { - if vpnProtocol.supportsGateway { - gatewayView - } - if vpnProtocol.supportsDNS { - dnsView - } - if vpnProtocol.supportsProxy { - proxyView - } - if vpnProtocol.supportsMTU { - mtuView - } - }.navigationTitle(L10n.NetworkSettings.title) - .toolbar { - CopySavingButton( - original: $currentProfile.value.networkSettings, - copy: $settings, - mapping: \.stripped, - label: themeSaveButtonLabel - ) - } - } -} - -// MARK: - - -// MARK: Gateway - -private extension NetworkSettingsView { - var gatewayView: some View { - Section { - Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticGateway.themeAnimation()) - - if !settings.isAutomaticGateway { - Toggle(Unlocalized.Network.ipv4, isOn: $settings.gateway.isDefaultIPv4) - Toggle(Unlocalized.Network.ipv6, isOn: $settings.gateway.isDefaultIPv6) - } - } header: { - Text(L10n.NetworkSettings.Gateway.title) - } - } -} - -// MARK: DNS - -private extension NetworkSettingsView { - - @ViewBuilder - var dnsView: some View { - Section { - Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticDNS.themeAnimation()) - - if !settings.isAutomaticDNS { - themeTextPicker( - L10n.Global.Strings.configuration, - selection: $settings.dns.configurationType, - values: Network.DNSSettings.availableConfigurationTypes(forVPNProtocol: vpnProtocol), - description: \.localizedDescription - ) - - switch settings.dns.configurationType { - case .plain: - EmptyView() - - case .https: - dnsManualHTTPSRow - - case .tls: - dnsManualTLSRow - - case .disabled: - EmptyView() - } - } - } header: { - Text(Unlocalized.Network.dns) - } - if !settings.isAutomaticDNS && settings.dns.configurationType != .disabled { - dnsManualServers - dnsManualDomainRow - dnsManualSearchDomains - } - } - - var dnsManualHTTPSRow: some View { - TextField(Unlocalized.Placeholders.dohURL, text: $settings.dns.dnsHTTPSURL.toString()) - .themeValidURL(settings.dns.dnsHTTPSURL?.absoluteString) - } - - var dnsManualTLSRow: some View { - TextField(Unlocalized.Placeholders.dotServerName, text: $settings.dns.dnsTLSServerName ?? "") - .themeValidDNSOverTLSServerName(settings.dns.dnsTLSServerName) - } - - var dnsManualServers: some View { - Section { - EditableTextList( - elements: $settings.dns.dnsServers ?? [], - allowsDuplicates: false, - mapping: mapNotEmpty - ) { - TextField( - Unlocalized.Placeholders.dnsAddress, - text: $0.text, - onEditingChanged: $0.onEditingChanged, - onCommit: $0.onCommit - ).themeValidIPAddress($0.text.wrappedValue) - } addLabel: { - Text(L10n.NetworkSettings.Items.AddDnsServer.caption) - } commitLabel: { - Text(L10n.Global.Strings.add) - } - } - } - - var dnsManualDomainRow: some View { - TextField(L10n.Global.Strings.domain, text: $settings.dns.dnsDomain ?? "") - .themeValidDomainName(settings.dns.dnsDomain) - } - - var dnsManualSearchDomains: some View { - Section { - EditableTextList( - elements: $settings.dns.dnsSearchDomains ?? [], - allowsDuplicates: false, - mapping: mapNotEmpty - ) { - TextField( - Unlocalized.Placeholders.dnsDomain, - text: $0.text, - onEditingChanged: $0.onEditingChanged, - onCommit: $0.onCommit - ).themeValidDomainName($0.text.wrappedValue) - } addLabel: { - Text(L10n.NetworkSettings.Items.AddDnsDomain.caption) - } commitLabel: { - Text(L10n.Global.Strings.add) - } - } - } -} - -// MARK: Proxy - -private extension NetworkSettingsView { - - @ViewBuilder - var proxyView: some View { - Section { - Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticProxy.themeAnimation()) - - if !settings.isAutomaticProxy { - themeTextPicker( - L10n.Global.Strings.configuration, - selection: $settings.proxy.configurationType, - values: Network.ProxySettings.availableConfigurationTypes, - description: \.localizedDescription - ) - - switch settings.proxy.configurationType { - case .manual: - TextField(Unlocalized.Placeholders.address, text: $settings.proxy.proxyAddress ?? "") - .themeValidIPAddress(settings.proxy.proxyAddress) - .withLeadingText(L10n.Global.Strings.address) - - TextField(Unlocalized.Placeholders.port, text: $settings.proxy.proxyPort.toString()) - .themeValidSocketPort(settings.proxy.proxyPort?.description) - .withLeadingText(L10n.Global.Strings.port) - - case .pac: - TextField(Unlocalized.Placeholders.pacURL, text: $settings.proxy.proxyAutoConfigurationURL.toString()) - .themeValidURL(settings.proxy.proxyAutoConfigurationURL?.absoluteString) - - case .disabled: - EmptyView() - } - } - } header: { - Text(L10n.Global.Strings.proxy) - } - if !settings.isAutomaticProxy && settings.proxy.configurationType == .manual { - proxyManualBypassDomains - } - } - - var proxyManualBypassDomains: some View { - Section { - EditableTextList( - elements: $settings.proxy.proxyBypassDomains ?? [], - allowsDuplicates: false, - mapping: mapNotEmpty - ) { - TextField( - Unlocalized.Placeholders.proxyBypassDomain, - text: $0.text, - onEditingChanged: $0.onEditingChanged, - onCommit: $0.onCommit - ).themeValidWildcardDomainName($0.text.wrappedValue) - } addLabel: { - Text(L10n.NetworkSettings.Items.AddProxyBypass.caption) - } commitLabel: { - Text(L10n.Global.Strings.add) - } - } - } -} - -// MARK: MTU - -private extension NetworkSettingsView { - var mtuView: some View { - Section { - Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticMTU.themeAnimation()) - - if !settings.isAutomaticMTU { - themeTextPicker( - L10n.Global.Strings.bytes, - selection: $settings.mtu.mtuBytes, - values: Network.MTUSettings.availableBytes, - description: { $0.localizedDescription(style: .mtu) } - ) - } - } header: { - Text(Unlocalized.Network.mtu) - } - } -} - -// MARK: Global - -private extension NetworkSettingsView { - var vpnProtocol: VPNProtocolType { - currentProfile.value.currentVPNProtocol - } - -// EditButton() -// .disabled(!isAnythingManual) - - var isAnythingManual: Bool { -// if settings.gateway.choice == .manual { -// return true -// } - if settings.dns.choice == .manual { - return true - } - if settings.proxy.choice == .manual { - return true - } -// if settings.mtu.choice == .manual { -// return true -// } - return false - } - - func mapNotEmpty(elements: [IdentifiableString]) -> [IdentifiableString] { - elements - .filter { !$0.string.isEmpty } - } -} diff --git a/Passepartout/App/Views/OnDemandView+SSID.swift b/Passepartout/App/Views/OnDemandView+SSID.swift deleted file mode 100644 index 62682863..00000000 --- a/Passepartout/App/Views/OnDemandView+SSID.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// OnDemandView+SSID.swift -// Passepartout -// -// Created by Davide De Rosa on 2/23/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension OnDemandView { - struct SSIDList: View { - @Binding var withSSIDs: [String: Bool] - - // FIXME: arch, this is a candidate for DI - @StateObject private var reader = Wifi(observer: CoreLocationWifiObserver()) - - var body: some View { - EditableTextList(elements: allSSIDs, allowsDuplicates: false, mapping: mapElements) { text in - requestSSID(text) - } textField: { - ssidRow(callback: $0) - } addLabel: { - Text(L10n.OnDemand.Items.AddSsid.caption) - } commitLabel: { - Text(L10n.Global.Strings.add) - } - } - } -} - -// MARK: - - -private extension OnDemandView.SSIDList { - func mapElements(elements: [IdentifiableString]) -> [IdentifiableString] { - elements - .filter { !$0.string.isEmpty } - .sorted { $0.string.lowercased() < $1.string.lowercased() } - } - - func ssidRow(callback: EditableTextFieldCallback) -> some View { - Group { - if callback.isNewElement { - ssidField(callback: callback) - } else { - Toggle(isOn: isSSIDOn(callback.text.wrappedValue)) { - ssidField(callback: callback) - } - } - } - } - - func ssidField(callback: EditableTextFieldCallback) -> some View { - TextField( - Unlocalized.Network.ssid, - text: callback.text, - onEditingChanged: callback.onEditingChanged, - onCommit: callback.onCommit - ).themeValidSSID(callback.text.wrappedValue) - } - - var allSSIDs: Binding<[String]> { - .init { - Array(withSSIDs.keys) - } set: { newValue in - withSSIDs.forEach { - guard newValue.contains($0.key) else { - withSSIDs.removeValue(forKey: $0.key) - return - } - } - newValue.forEach { - guard withSSIDs[$0] == nil else { - return - } - withSSIDs[$0] = false - } -// print(">>> withSSIDs (allSSIDs): \(withSSIDs)") - } - } - - var onSSIDs: Binding> { - .init { - Set(withSSIDs.filter { - $0.value - }.map(\.key)) - } set: { newValue in - withSSIDs.forEach { - guard newValue.contains($0.key) else { - if withSSIDs[$0.key] != nil { - withSSIDs[$0.key] = false - } else { - withSSIDs.removeValue(forKey: $0.key) - } - return - } - } - newValue.forEach { - guard !(withSSIDs[$0] ?? false) else { - return - } - withSSIDs[$0] = true - } -// print(">>> withSSIDs (onSSIDs): \(withSSIDs)") - } - } - - func isSSIDOn(_ ssid: String) -> Binding { - .init { - withSSIDs[ssid] ?? false - } set: { - withSSIDs[ssid] = $0 - } - } -} - -// MARK: - - -private extension OnDemandView.SSIDList { - func requestSSID(_ text: Binding) { - Task { @MainActor in - let ssid = try await reader.currentSSID() - if !withSSIDs.keys.contains(ssid) { - text.wrappedValue = ssid - } - } - } -} diff --git a/Passepartout/App/Views/OnDemandView.swift b/Passepartout/App/Views/OnDemandView.swift deleted file mode 100644 index cf82bc8d..00000000 --- a/Passepartout/App/Views/OnDemandView.swift +++ /dev/null @@ -1,156 +0,0 @@ -// -// OnDemandView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/23/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct OnDemandView: View { - @ObservedObject private var productManager: ProductManager - - @ObservedObject private var currentProfile: ObservableProfile - - @State private var onDemand = Profile.OnDemand() - - init(currentProfile: ObservableProfile) { - productManager = .shared - self.currentProfile = currentProfile - } - - var body: some View { - debugChanges() - return List { - enabledView - if onDemand.isEnabled && onDemand.policy != .any { - mainView - } - }.navigationTitle(L10n.OnDemand.title) - .toolbar { - CopySavingButton( - original: $currentProfile.value.onDemand, - copy: $onDemand, - mapping: \.stripped, - label: themeSaveButtonLabel - ) - } - - // Siri - .onChange(of: onDemand.withMobileNetwork, perform: donateMobileIntent) - .onChange(of: onDemand.withSSIDs, perform: donateNetworkIntents) - } -} - -// MARK: - - -private extension OnDemandView { - var enabledView: some View { - Section { - Toggle(L10n.Global.Strings.enabled, isOn: $onDemand.isEnabled.themeAnimation()) - if onDemand.isEnabled { - themeTextPicker( - L10n.Global.Strings.policy, - selection: $onDemand.policy, - values: [.any, .including, .excluding], - description: \.localizedDescription - ) - } - } footer: { - Text(policyFooterDescription) - } - } - - var policyFooterDescription: String { - guard onDemand.isEnabled else { - return "" // better animation than removing footer completely - } - let suffix: String - switch onDemand.policy { - case .any: - suffix = L10n.OnDemand.Sections.Policy.Footer.any - - case .including, .excluding: - if onDemand.policy == .including { - suffix = L10n.OnDemand.Sections.Policy.Footer.including - } else { - suffix = L10n.OnDemand.Sections.Policy.Footer.excluding - } - } - return L10n.OnDemand.Sections.Policy.footer(suffix) - } - - @ViewBuilder - var mainView: some View { - if Utils.hasCellularData() { - Section { - Toggle(L10n.OnDemand.Items.Mobile.caption, isOn: $onDemand.withMobileNetwork) - } header: { - Text(L10n.Global.Strings.networks) - } - } else if Utils.hasEthernet() { - Section { - Toggle(L10n.OnDemand.Items.Ethernet.caption, isOn: $onDemand.withEthernetNetwork) - } header: { - Text(L10n.Global.Strings.networks) - } - } - Section { - SSIDList(withSSIDs: $onDemand.withSSIDs) - } header: { - if !Utils.hasCellularData() && !Utils.hasEthernet() { - Text(L10n.Global.Strings.networks) - } - } - } - - var isEligibleForSiri: Bool { - productManager.isEligible(forFeature: .siriShortcuts) - } -} - -// MARK: - - -private extension OnDemandView { - - // eligibility: donate intents if eligible for Siri - func donateMobileIntent(_ isEnabled: Bool) { - guard isEligibleForSiri else { - return - } - #if !os(tvOS) - IntentDispatcher.donateTrustCellularNetwork() - IntentDispatcher.donateUntrustCellularNetwork() - #endif - } - - // eligibility: donate intents if eligible for Siri - func donateNetworkIntents(_: [String: Bool]) { - guard isEligibleForSiri else { - return - } - #if !os(tvOS) - IntentDispatcher.donateTrustCurrentNetwork() - IntentDispatcher.donateUntrustCurrentNetwork() - #endif - } -} diff --git a/Passepartout/App/Views/OrganizerView+ProfileRow.swift b/Passepartout/App/Views/OrganizerView+ProfileRow.swift deleted file mode 100644 index 5a257a92..00000000 --- a/Passepartout/App/Views/OrganizerView+ProfileRow.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// OrganizerView+ProfileRow.swift -// Passepartout -// -// Created by Davide De Rosa on 4/28/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI - -extension OrganizerView { - struct ProfileRow: View { - private let profile: Profile - - private let isActiveProfile: Bool - - @Binding private var modalType: ModalType? - - init(profile: Profile, isActiveProfile: Bool, modalType: Binding) { - self.profile = profile - self.isActiveProfile = isActiveProfile - _modalType = modalType - } - - var body: some View { - debugChanges() - return HStack { - VStack(alignment: .leading, spacing: 5) { - Text(profile.header.name) - .themeCellTitleStyle() - .themeLongTextStyle() - - VPNStatusText(isActiveProfile: isActiveProfile) - .themeCellSubtitleStyle() - .themeSecondaryTextStyle() - } - Spacer() - VPNToggle( - profile: profile, - interactiveProfile: interactiveProfile, - title: L10n.Global.Strings.enabled, - rateLimit: Constants.RateLimit.vpnToggle - ).labelsHidden() - }.padding([.top, .bottom], 10) - } - } -} - -// MARK: - - -private extension OrganizerView.ProfileRow { - var interactiveProfile: Binding { - .init { - if case .interactiveAccount(let profile) = modalType { - return profile - } - return nil - } set: { - if let profile = $0 { - modalType = .interactiveAccount(profile: profile) - } else { - modalType = nil - } - } - } -} -#endif diff --git a/Passepartout/App/Views/OrganizerView+Profiles.swift b/Passepartout/App/Views/OrganizerView+Profiles.swift deleted file mode 100644 index 5d72985c..00000000 --- a/Passepartout/App/Views/OrganizerView+Profiles.swift +++ /dev/null @@ -1,185 +0,0 @@ -// -// OrganizerView+Profiles.swift -// Passepartout -// -// Created by Davide De Rosa on 5/3/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI - -extension OrganizerView { - struct ProfilesList: View { - @ObservedObject private var profileManager: ProfileManager - - @Binding private var modalType: ModalType? - - init(modalType: Binding) { - profileManager = .shared - _modalType = modalType - } - - var body: some View { - debugChanges() - return Group { - mainView - if !profileManager.hasProfiles { - emptyView - } - }.onAppear(perform: performMigrationsIfNeeded) - .onReceive(profileManager.didCreateProfile) { - profileManager.currentProfileId = $0.id - } - } - } -} - -extension OrganizerView { - struct ProfileContextMenu: View { - @ObservedObject private var profileManager: ProfileManager - - @ObservedObject private var currentVPNState: ObservableVPNState - - let header: Profile.Header - - init(header: Profile.Header) { - profileManager = .shared - currentVPNState = .shared - self.header = header - } - - var body: some View { - if profileManager.isActiveProfile(header.id) { - reconnectButton - } - duplicateButton - deleteButton - } - } -} - -// MARK: - - -private extension OrganizerView.ProfilesList { - var mainView: some View { - List { - if profileManager.hasProfiles { - - // FIXME: iPad multitasking, navigation binding does not clear on pop - // - if listStyle is different than .sidebar - // - if listStyle is .sidebar but List has no Section - if themeIsiPadMultitasking { - Section { - profilesView - } header: { - Text(L10n.Global.Strings.profiles) - } - } else { - profilesView - } - } - } - .themeAnimation(on: profileManager.headers) - } - - var profilesView: some View { - ForEach(sortedProfiles, content: profileRow(forProfile:)) - .onDelete(perform: removeProfiles) - } - - var emptyView: some View { - VStack { - Text(L10n.Organizer.Empty.noProfiles) - .themeInformativeTextStyle() - } - } - - func profileRow(forProfile profile: Profile) -> some View { - NavigationLink(tag: profile.id, selection: $profileManager.currentProfileId) { - ProfileView() - } label: { - profileLabel(forProfile: profile) - } - .contextMenu { - OrganizerView.ProfileContextMenu(header: profile.header) - } - .themeListSelectionColor(isSelected: profileManager.isCurrentProfile(profile.id)) - } - - func profileLabel(forProfile profile: Profile) -> some View { - OrganizerView.ProfileRow( - profile: profile, - isActiveProfile: profileManager.isActiveProfile(profile.id), - modalType: $modalType - ) - } - - var sortedProfiles: [Profile] { - profileManager.profiles - .sorted() - } -} - -private extension OrganizerView.ProfileContextMenu { - var reconnectButton: some View { - ProfileView.ReconnectButton() - } - - var duplicateButton: some View { - ProfileView.DuplicateButton( - header: header, - setAsCurrent: false - ) - } - - var deleteButton: some View { - Button(role: .destructive) { - withAnimation { - profileManager.removeProfiles(withIds: [header.id]) - } - } label: { - Label(L10n.Global.Strings.delete, systemImage: themeDeleteImage) - } - } -} - -// MARK: - - -private extension OrganizerView.ProfilesList { - func removeProfiles(at offsets: IndexSet) { - let currentHeaders = sortedProfiles - var toDelete: [UUID] = [] - offsets.forEach { - toDelete.append(currentHeaders[$0].id) - } - withAnimation { - profileManager.removeProfiles(withIds: toDelete) - } - } - - func performMigrationsIfNeeded() { - Task { @MainActor in - UpgradeManager.shared.migrateData(profileManager: profileManager) - } - } -} -#endif diff --git a/Passepartout/App/Views/OrganizerView+Scene.swift b/Passepartout/App/Views/OrganizerView+Scene.swift deleted file mode 100644 index 4ccb7732..00000000 --- a/Passepartout/App/Views/OrganizerView+Scene.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// OrganizerView+Scene.swift -// Passepartout -// -// Created by Davide De Rosa on 4/2/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI - -extension OrganizerView { - struct SceneView: View { - @Environment(\.scenePhase) private var scenePhase - - @Binding private var isAlertPresented: Bool - - @Binding private var alertType: AlertType? - - @Binding private var didHandleSubreddit: Bool - - @State private var isFirstLaunch = true - - init(isAlertPresented: Binding, alertType: Binding, didHandleSubreddit: Binding) { - _isAlertPresented = isAlertPresented - _alertType = alertType - _didHandleSubreddit = didHandleSubreddit - } - - var body: some View { - - // dummy text, EmptyView() does not trigger on*() handlers - Text("Scene") - .hidden() - .onAppear(perform: onAppear) - } - } -} - -// MARK: - - -private extension OrganizerView.SceneView { - - @MainActor - func onAppear() { - guard didHandleSubreddit else { - alertType = .subscribeReddit - isAlertPresented = true - return - } - - // - // FIXME: iPad portrait/compact, loading current profile adds ProfileView() twice - // - // - from MainView - // - from NavigationLink destination in OrganizerView - // - // can notice becase "Back" needs to be tapped twice to show sidebar - // workaround: set active profile but do not load as current (prevents NavigationLink activation) - // - guard isFirstLaunch else { - return - } - isFirstLaunch = false - if themeIdiom != .phone && !themeIsiPadPortrait, let activeProfileId = ProfileManager.shared.activeProfileId { - ProfileManager.shared.currentProfileId = activeProfileId - } - } -} -#endif diff --git a/Passepartout/App/Views/OrganizerView.swift b/Passepartout/App/Views/OrganizerView.swift deleted file mode 100644 index 8fffbfd0..00000000 --- a/Passepartout/App/Views/OrganizerView.swift +++ /dev/null @@ -1,177 +0,0 @@ -// -// OrganizerView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/6/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import PassepartoutLibrary -import SwiftUI - -struct OrganizerView: View { - enum ModalType: Identifiable { - case interactiveAccount(profile: Profile) - - // XXX: alert ids - var id: Int { - switch self { - case .interactiveAccount: return 1 - } - } - } - - enum AlertType: Identifiable { - case subscribeReddit - - // XXX: alert ids - var id: Int { - switch self { - case .subscribeReddit: return 1 - } - } - } - - @State private var addProfileModalType: AddProfileMenu.ModalType? - - @State private var modalType: ModalType? - - @State private var isAlertPresented = false - - @State private var alertType: AlertType? - - @State private var isHostFileImporterPresented = false - - @AppStorage(AppPreference.didHandleSubreddit.key) private var didHandleSubreddit = false - - private let hostFileTypes = Constants.URLs.filetypes - - private let redditURL = Constants.URLs.subreddit - - var body: some View { - debugChanges() - return ZStack { - hiddenSceneView - ProfilesList(modalType: $modalType) - }.toolbar { - ToolbarItem(placement: .primaryAction) { - AddProfileMenu( - modalType: $addProfileModalType, - isHostFileImporterPresented: $isHostFileImporterPresented - ) - } - ToolbarItem(placement: .navigation) { - if themeIdiom == .phone { - SettingsButton() - } - } - }.sheet(item: $modalType, content: presentedModal) - .alert( - Unlocalized.appName, - isPresented: $isAlertPresented, - presenting: alertType, - actions: alertActions, - message: alertMessage - ) - .fileImporter( - isPresented: $isHostFileImporterPresented, - allowedContentTypes: hostFileTypes, - allowsMultipleSelection: false, - onCompletion: onHostFileImporterResult - ) - .onOpenURL(perform: onOpenURL) - .themePrimaryView() - } -} - -// MARK: - - -private extension OrganizerView { - var hiddenSceneView: some View { - SceneView( - isAlertPresented: $isAlertPresented, - alertType: $alertType, - didHandleSubreddit: $didHandleSubreddit - ) - } - - @ViewBuilder - func presentedModal(_ modalType: ModalType) -> some View { - switch modalType { - case .interactiveAccount(let profile): - NavigationView { - InteractiveConnectionView(profile: profile) - }.themeGlobal() - } - } - - func alertActions(_ alertType: AlertType) -> some View { - switch alertType { - case .subscribeReddit: - return Group { - Button(L10n.Organizer.Alerts.Reddit.Buttons.subscribe) { - didHandleSubreddit = true - URL.open(redditURL) - } - Button(role: .cancel) { - didHandleSubreddit = true - } label: { - Text(L10n.Global.Alerts.Buttons.never) - } - } - } - } - - func alertMessage(_ alertType: AlertType) -> some View { - switch alertType { - case .subscribeReddit: - return Text(L10n.Organizer.Alerts.Reddit.message) - } - } -} - -// MARK: - - -private extension OrganizerView { - - @MainActor - func onHostFileImporterResult(_ result: Result<[URL], Error>) { - switch result { - case .success(let urls): - guard let url = urls.first else { - assertionFailure("Empty URLs from file importer?") - return - } - Task { - await Task.maybeWait(forMilliseconds: Constants.Delays.xxxPresentFileImporter) - addProfileModalType = .addHost(url, false) - } - - case .failure(let error): - ErrorHandler.shared.handle(error, title: L10n.Menu.Contextual.AddProfile.fromFiles) - } - } - - func onOpenURL(_ url: URL) { - addProfileModalType = .addHost(url, false) - } -} -#endif diff --git a/Passepartout/App/Views/PaywallView+Purchase.swift b/Passepartout/App/Views/PaywallView+Purchase.swift deleted file mode 100644 index ad83e351..00000000 --- a/Passepartout/App/Views/PaywallView+Purchase.swift +++ /dev/null @@ -1,322 +0,0 @@ -// -// PaywallView+Purchase.swift -// Passepartout -// -// Created by Davide De Rosa on 3/12/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension PaywallView { - struct PurchaseView: View { - fileprivate enum PurchaseState { - case purchasing(InAppProduct) - - case restoring - } - - @Environment(\.scenePhase) private var scenePhase - - @ObservedObject private var productManager: ProductManager - - @Binding private var isPresented: Bool - - private let feature: LocalProduct? - - @State private var purchaseState: PurchaseState? - - @State private var didPurchaseAppleTV = false - - init(isPresented: Binding, feature: LocalProduct? = nil) { - productManager = .shared - _isPresented = isPresented - self.feature = feature - } - - var body: some View { - List { - if feature != .appleTV { - skFullVersion.map { - fullFeaturesSection(withHeader: $0.localizedTitle) - } - } - purchaseSection - .disabled(purchaseState != nil) - restoreSection - .disabled(purchaseState != nil) - } - .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 { - await productManager.refreshProducts() - } - .onChange(of: scenePhase) { newValue in - if newValue == .active { - Task { - await productManager.refreshProducts() - } - } - } - .themeAnimation(on: productManager.isRefreshingProducts) - } - } -} - -private struct FeatureModel: Identifiable, Comparable { - let productIdentifier: String - - let title: String - - let subtitle: String? - - init(localProduct: LocalProduct, title: String) { - productIdentifier = localProduct.rawValue - self.title = title - subtitle = nil - } - - init(product: InAppProduct) { - productIdentifier = product.productIdentifier - title = product.localizedTitle - let description = product.localizedDescription - subtitle = description != title ? description : nil - } - - var id: String { - productIdentifier - } - - static func < (lhs: Self, rhs: Self) -> Bool { - lhs.title.lowercased() < rhs.title.lowercased() - } -} - -private struct PurchaseRow: View { - var product: InAppProduct? - - let title: String - - let action: () -> Void - - let purchaseState: PaywallView.PurchaseView.PurchaseState? - - var body: some View { - actionButton - } -} - -// MARK: - - -private extension PaywallView.PurchaseView { - func fullFeaturesSection(withHeader header: String) -> some View { - Section { - ForEach(fullFeatures) { feature in - VStack(alignment: .leading) { - Text(feature.title) - .themeCellTitleStyle() - feature.subtitle.map { - Text($0) - .themeCellSubtitleStyle() - .themeSecondaryTextStyle() - } - } - } - } header: { - Text(header) - } - } - - var purchaseSection: some View { - Section { - if !productManager.isRefreshingProducts { - ForEach(productRowModels, id: \.productIdentifier, content: productRow) - } else { - ProgressView() - } - } header: { - Text(L10n.Paywall.title) - } footer: { - Text(L10n.Paywall.Sections.Products.footer) - } - } - - var restoreSection: some View { - Section { - restoreRow - } footer: { - Text(L10n.Paywall.Items.Restore.description) - } - } - - func productRow(_ product: InAppProduct) -> some View { - PurchaseRow( - product: product, - title: product.localizedTitle, - action: { - purchaseProduct(product) - }, - purchaseState: purchaseState - ) - } - - var restoreRow: some View { - PurchaseRow( - title: L10n.Paywall.Items.Restore.title, - action: restorePurchases, - purchaseState: purchaseState - ) - } -} - -private extension PaywallView.PurchaseView { - var skFullVersion: InAppProduct? { - productManager.product(withIdentifier: .fullVersion) - } - - var fullFeatures: [FeatureModel] { - productManager.featureProducts(excluding: { - $0 == .fullVersion || $0 == .appleTV || $0.isLegacyPlatformVersion - }) - .map { - FeatureModel(product: $0) - } - .sorted() - } - - var productRowModels: [InAppProduct] { - productManager - .purchasableProducts(withFeature: feature) - .compactMap { productManager.product(withIdentifier: $0) } - } -} - -private extension PurchaseRow { - - @ViewBuilder - var actionButton: some View { - if let product { - purchaseButton(product) - } else { - restoreButton - } - } - - func purchaseButton(_ product: InAppProduct) -> some View { - HStack { - Button(title, action: action) - Spacer() - if case .purchasing(let pending) = purchaseState, pending.productIdentifier == product.productIdentifier { - ProgressView() - } else { - product.localizedPrice.map { - Text($0) - .themeSecondaryTextStyle() - } - } - } - } - - var restoreButton: some View { - HStack { - Button(title, action: action) - Spacer() - if case .restoring = purchaseState { - ProgressView() - } - } - } -} - -// 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 - } - purchaseState = nil - } catch { - pp_log.error("Unable to purchase: \(error)") - ErrorHandler.shared.handle( - title: product.localizedTitle, - message: AppError(error).localizedDescription - ) { - purchaseState = nil - } - } - } - } - - func restorePurchases() { - purchaseState = .restoring - - 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)") - ErrorHandler.shared.handle( - title: L10n.Paywall.Items.Restore.title, - message: AppError(error).localizedDescription - ) { - purchaseState = nil - } - } - } - } -} diff --git a/Passepartout/App/Views/PaywallView+Restricted.swift b/Passepartout/App/Views/PaywallView+Restricted.swift deleted file mode 100644 index c3a3897c..00000000 --- a/Passepartout/App/Views/PaywallView+Restricted.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// PaywallView+Restricted.swift -// Passepartout -// -// Created by Davide De Rosa on 3/22/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import SwiftUI - -extension PaywallView { - struct RestrictedView: View { - var body: some View { - Text("The requested feature in unavailable in this build.") - .multilineTextAlignment(.center) - .padding() - .navigationTitle("Restricted") - } - } -} diff --git a/Passepartout/App/Views/PaywallView.swift b/Passepartout/App/Views/PaywallView.swift deleted file mode 100644 index 0ee668a5..00000000 --- a/Passepartout/App/Views/PaywallView.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// PaywallView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/12/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct PaywallView: View { - @ObservedObject private var productManager: ProductManager - - @Binding private var isPresented: Bool - - private let feature: LocalProduct - - init(modalType: Binding, feature: LocalProduct) { - let isPresented = Binding { - modalType.wrappedValue != nil - } set: { - if !$0 { - modalType.wrappedValue = nil - } - } - self.init(isPresented: isPresented, feature: feature) - } - - init(isPresented: Binding, feature: LocalProduct) { - productManager = .shared - _isPresented = isPresented - self.feature = feature - } - - var body: some View { - Group { - if productManager.appType.isRestricted { - RestrictedView() - } else { - PurchaseView( - isPresented: $isPresented, - feature: feature - ) - } - }.toolbar { - themeCloseItem(isPresented: $isPresented) - }.themeSecondaryView() - } -} diff --git a/Passepartout/App/Views/ProfileView+Configuration.swift b/Passepartout/App/Views/ProfileView+Configuration.swift deleted file mode 100644 index ac76fd87..00000000 --- a/Passepartout/App/Views/ProfileView+Configuration.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// ProfileView+Configuration.swift -// Passepartout -// -// Created by Davide De Rosa on 3/27/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension ProfileView { - struct ConfigurationSection: View { - @ObservedObject private var productManager: ProductManager - - @ObservedObject private var currentProfile: ObservableProfile - - @Binding private var modalType: ModalType? - - init(currentProfile: ObservableProfile, modalType: Binding) { - productManager = .shared - self.currentProfile = currentProfile - _modalType = modalType - } - - var body: some View { - Section { - if currentProfile.value.vpnProtocols.count > 1 { - themeTextPicker( - L10n.Global.Strings.protocol, - selection: $currentProfile.value.currentVPNProtocol, - values: currentProfile.value.vpnProtocols, - description: \.description - ) - } else { - 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( - providerName: currentProfile.value.header.providerName, - vpnProtocol: currentProfile.value.currentVPNProtocol, - account: $currentProfile.value.account - ) - } label: { - Label(L10n.Account.title, systemImage: themeAccountImage) - } - } - - // eligibility: enter network settings or present paywall - if isEligibleForNetworkSettings { - NavigationLink { - NetworkSettingsView(currentProfile: currentProfile) - } label: { - networkSettingsRow - } - } else { - Button { - modalType = .paywallNetworkSettings - } label: { - networkSettingsRow - } - } - - // eligibility: enter trusted networks or present paywall - if isEligibleForTrustedNetworks { - NavigationLink { - OnDemandView(currentProfile: currentProfile) - } label: { - onDemandRow - } - } else { - Button { - modalType = .paywallTrustedNetworks - } label: { - onDemandRow - } - } - } header: { - Text(L10n.Global.Strings.configuration) - } - } - } -} - -// MARK: - - -private extension ProfileView.ConfigurationSection { - var networkSettingsRow: some View { - Label(L10n.NetworkSettings.title, systemImage: themeNetworkSettingsImage) - } - - var onDemandRow: some View { - Label(L10n.OnDemand.title, systemImage: themeOnDemandImage) - } - - var isEligibleForNetworkSettings: Bool { - productManager.isEligible(forFeature: .networkSettings) - } - - var isEligibleForTrustedNetworks: Bool { - productManager.isEligible(forFeature: .trustedNetworks) - } -} diff --git a/Passepartout/App/Views/ProfileView+MainMenu.swift b/Passepartout/App/Views/ProfileView+MainMenu.swift deleted file mode 100644 index 6b7d5d40..00000000 --- a/Passepartout/App/Views/ProfileView+MainMenu.swift +++ /dev/null @@ -1,287 +0,0 @@ -// -// ProfileView+MainMenu.swift -// Passepartout -// -// Created by Davide De Rosa on 2/6/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension ProfileView { - struct MainMenu: View { - enum AlertType: Int, Identifiable { - case uninstallVPN - - case deleteProfile - - var id: Int { - rawValue - } - } - - @ObservedObject private var profileManager: ProfileManager - - @ObservedObject private var vpnManager: VPNManager - - @ObservedObject private var currentVPNState: ObservableVPNState - - @ObservedObject private var currentProfile: ObservableProfile - - @Binding private var modalType: ModalType? - - @State private var isAlertPresented = false - - @State private var alertType: AlertType? - - private let uninstallVPNTitle = L10n.Global.Strings.uninstall - - private let deleteProfileTitle = L10n.Global.Strings.delete - - init(currentProfile: ObservableProfile, modalType: Binding) { - profileManager = .shared - vpnManager = .shared - currentVPNState = .shared - self.currentProfile = currentProfile - _modalType = modalType - } - - var body: some View { - mainView - .alert( - Text(Unlocalized.appName), - isPresented: $isAlertPresented, - presenting: alertType, - actions: alertActions, - message: alertMessage - ) - } - } -} - -extension ProfileView { - struct ReconnectButton: View { - @ObservedObject private var vpnManager: VPNManager - - init() { - vpnManager = .shared - } - - var body: some View { - Button { - Task { @MainActor in - await vpnManager.reconnect() - } - } label: { - Label(L10n.Global.Strings.reconnect, systemImage: themeReconnectImage) - } - } - } - - #if !os(tvOS) - struct ShortcutsButton: View { - @ObservedObject private var productManager: ProductManager - - @Binding private var modalType: ModalType? - - init(modalType: Binding) { - productManager = .shared - _modalType = modalType - } - - var body: some View { - Button { - presentShortcutsOrPaywall() - } label: { - Label(Unlocalized.Other.siri, systemImage: themeShortcutsImage) - } - } - } - #endif - - struct RenameButton: View { - @Binding private var modalType: ModalType? - - init(modalType: Binding) { - _modalType = modalType - } - - var body: some View { - Button { - modalType = .rename - } label: { - Label(L10n.Global.Strings.rename, systemImage: themeRenameProfileImage) - } - } - } - - struct DuplicateButton: View { - @ObservedObject private var profileManager: ProfileManager - - private let header: Profile.Header - - private let setAsCurrent: Bool - - init(header: Profile.Header, setAsCurrent: Bool) { - profileManager = .shared - self.header = header - self.setAsCurrent = setAsCurrent - } - - var body: some View { - Button { - duplicateProfile(withId: header.id) - } label: { - Label(L10n.Global.Strings.duplicate, systemImage: themeDuplicateImage) - } - } - } -} - -// MARK: - - -private extension ProfileView.MainMenu { - var header: Profile.Header { - currentProfile.value.header - } - - var mainView: some View { - Menu { - ProfileView.ReconnectButton() - #if !os(tvOS) - ProfileView.ShortcutsButton( - modalType: $modalType - ) - #endif - Divider() - ProfileView.RenameButton( - modalType: $modalType - ) - ProfileView.DuplicateButton( - header: header, - setAsCurrent: true - ) - uninstallVPNButton - Divider() - deleteProfileButton - } label: { - themeSettingsMenuImage.asSystemImage - } - } - - func alertActions(_ alertType: AlertType) -> some View { - switch alertType { - case .uninstallVPN: - return Group { - Button(role: .destructive, action: uninstallVPN) { - Text(uninstallVPNTitle) - } - Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.cancel) - } - } - - case .deleteProfile: - return Group { - Button(role: .destructive, action: removeProfile) { - Text(deleteProfileTitle) - } - Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.cancel) - } - } - } - } - - func alertMessage(_ alertType: AlertType) -> some View { - switch alertType { - case .uninstallVPN: - return Text(L10n.Profile.Alerts.UninstallVpn.message) - - case .deleteProfile: - return Text(L10n.Organizer.Alerts.RemoveProfile.message(header.name)) - } - } - - var uninstallVPNButton: some View { - Button { - alertType = .uninstallVPN - isAlertPresented = true - } label: { - Label(uninstallVPNTitle, systemImage: themeUninstallImage) - } - } - - var deleteProfileButton: some View { - Button(role: .destructive) { - alertType = .deleteProfile - isAlertPresented = true - } label: { - Label(deleteProfileTitle, systemImage: themeDeleteImage) - } - } -} - -#if !os(tvOS) -private extension ProfileView.ShortcutsButton { - var isEligibleForSiri: Bool { - productManager.isEligible(forFeature: .siriShortcuts) - } -} -#endif - -// MARK: - - -private extension ProfileView.MainMenu { - func uninstallVPN() { - Task { @MainActor in - await vpnManager.uninstall() - } - } - - func removeProfile() { - withAnimation { - profileManager.removeProfiles(withIds: [header.id]) - } - } -} - -#if !os(tvOS) -private extension ProfileView.ShortcutsButton { - func presentShortcutsOrPaywall() { - - // eligibility: enter Siri shortcuts or present paywall - if isEligibleForSiri { - modalType = .shortcuts - } else { - modalType = .paywallShortcuts - } - } -} -#endif - -private extension ProfileView.DuplicateButton { - func duplicateProfile(withId id: UUID) { - profileManager.duplicateProfile(withId: id, setAsCurrent: setAsCurrent) - } -} diff --git a/Passepartout/App/Views/ProfileView+Provider.swift b/Passepartout/App/Views/ProfileView+Provider.swift deleted file mode 100644 index e166fdcf..00000000 --- a/Passepartout/App/Views/ProfileView+Provider.swift +++ /dev/null @@ -1,167 +0,0 @@ -// -// ProfileView+Provider.swift -// Passepartout -// -// Created by Davide De Rosa on 3/18/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension ProfileView { - struct ProviderSection: View, ProviderProfileAvailability { - @ObservedObject var providerManager: ProviderManager - - @ObservedObject private var currentProfile: ObservableProfile - - @State private var isProviderLocationPresented = false - - @State private var isRefreshingInfrastructure = false - - var profile: Profile { - currentProfile.value - } - - init(currentProfile: ObservableProfile) { - providerManager = .shared - self.currentProfile = currentProfile - } - - var body: some View { - debugChanges() - return Group { - if isProviderProfileAvailable { - mainView - } else { - EmptyView() - } - } - } - } -} - -// MARK: - - -private extension ProfileView.ProviderSection { - - @ViewBuilder - var mainView: some View { - Section { - NavigationLink(isActive: $isProviderLocationPresented) { - ProviderLocationView( - currentProfile: currentProfile, - isEditable: true, - isPresented: $isProviderLocationPresented - ) - } label: { - HStack { - Label(L10n.Provider.Location.title, systemImage: themeProviderLocationImage) - Spacer() - currentProviderCountryImage - } - } - } header: { - currentProviderFullName.map(Text.init) - } footer: { - currentProviderServerDescription.map(Text.init) - } - Section { - Toggle( - L10n.Profile.Items.RandomizesServer.caption, - isOn: $currentProfile.value.providerRandomizesServer ?? false - ) - Toggle( - L10n.Profile.Items.VpnResolvesHostname.caption, - isOn: $currentProfile.value.networkSettings.resolvesHostname - ) - } footer: { - Text(L10n.Profile.Sections.VpnResolvesHostname.footer) - .xxxThemeTruncation() - } - Section { - NavigationLink { - ProviderPresetView(currentProfile: currentProfile) - } label: { - Label(L10n.Provider.Preset.title, systemImage: themeProviderPresetImage) - .withTrailingText(currentProviderPreset) - } - Button { - refreshInfrastructure() - } label: { - Text(L10n.Profile.Items.Provider.Refresh.caption) - }.withTrailingProgress(when: isRefreshingInfrastructure) - } footer: { - lastInfrastructureUpdate.map { - Text(L10n.Profile.Sections.ProviderInfrastructure.footer($0)) - } - } - } - - var currentProviderFullName: String? { - guard let name = profile.header.providerName else { - assertionFailure("Provider name accessed but profile is not a provider (isPlaceholder? \(profile.isPlaceholder))") - return nil - } - guard let metadata = providerManager.provider(withName: name) else { - assertionFailure("Provider metadata not found") - return nil - } - return metadata.fullName - } - - var currentProviderServerDescription: String? { - guard let server = profile.providerServer(providerManager) else { - return nil - } - if currentProfile.value.providerRandomizesServer ?? false { - return server.localizedDescription(style: .countryWithCategory(withCategory: true)) - } else { - return server.localizedDescription(style: .longWithCategory(withCategory: true)) - } - } - - var currentProviderCountryImage: Image? { - guard let code = profile.providerServer(providerManager)?.countryCode else { - return nil - } - return themeAssetsCountryImage(code).asAssetImage - } - - var currentProviderPreset: String? { - providerManager.localizedDescription(optionalStyle: .preset(profile: profile)) - } - - var lastInfrastructureUpdate: String? { - providerManager.localizedDescription(optionalStyle: .infrastructureUpdate(profile: profile)) - } -} - -// MARK: - - -private extension ProfileView.ProviderSection { - func refreshInfrastructure() { - isRefreshingInfrastructure = true - Task { @MainActor in - try? await providerManager.fetchRemoteProviderPublisher(forProfile: profile).async() - isRefreshingInfrastructure = false - } - } -} diff --git a/Passepartout/App/Views/ProfileView+Rename.swift b/Passepartout/App/Views/ProfileView+Rename.swift deleted file mode 100644 index 11709883..00000000 --- a/Passepartout/App/Views/ProfileView+Rename.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// ProfileView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/6/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension ProfileView { - struct RenameView: View { - enum Field { - case name - } - - @Environment(\.presentationMode) private var presentationMode - - @ObservedObject private var profileManager: ProfileManager - - @ObservedObject private var currentProfile: ObservableProfile - - @State private var newName = "" - - @State private var isOverwritingExistingProfile = false - - @FocusState private var focusedField: Field? - - init(currentProfile: ObservableProfile) { - profileManager = .shared - self.currentProfile = currentProfile - } - - var body: some View { - List { - Section { - TextField(L10n.Global.Placeholders.profileName, text: $newName, onCommit: commitRenaming) - .themeValidProfileName() - .focused($focusedField, equals: .name) - .onAppear(perform: loadCurrentName) - } header: { - Text(L10n.Profile.Alerts.Rename.title) - } - }.themeSecondaryView() - .navigationTitle(currentProfile.value.header.name) - .toolbar { - themeCloseItem(presentationMode: presentationMode) - ToolbarItem(placement: .primaryAction) { - Button(action: commitRenaming, label: themeSaveButtonLabel) - } - }.alert( - L10n.Profile.Alerts.Rename.title, - isPresented: $isOverwritingExistingProfile, - actions: alertOverwriteActions, - message: alertOverwriteMessage - ) - } - } -} - -// MARK: - - -private extension ProfileView.RenameView { - - @ViewBuilder - func alertOverwriteActions() -> some View { - Button(role: .destructive) { - commitRenaming(force: true) - } label: { - Text(L10n.Global.Strings.ok) - } - Button(role: .cancel) { - } label: { - Text(L10n.Global.Strings.cancel) - } - } - - func alertOverwriteMessage() -> some View { - Text(L10n.AddProfile.Shared.Alerts.Overwrite.message) - } -} - -// MARK: - - -private extension ProfileView.RenameView { - func loadCurrentName() { - newName = currentProfile.value.header.name - focusedField = .name - } - - func commitRenaming() { - commitRenaming(force: false) - } - - func commitRenaming(force: Bool) { - let name = newName.stripped - - guard !name.isEmpty else { - return - } - guard name != currentProfile.value.header.name else { - presentationMode.wrappedValue.dismiss() - return - } - guard force || !profileManager.isExistingProfile(withName: name) else { - isOverwritingExistingProfile = true - return - } - - let renamed = currentProfile.value.renamed(to: name) - profileManager.saveProfile(renamed, isActive: nil) - - presentationMode.wrappedValue.dismiss() - } -} diff --git a/Passepartout/App/Views/ProfileView+TV.swift b/Passepartout/App/Views/ProfileView+TV.swift deleted file mode 100644 index 9febdba8..00000000 --- a/Passepartout/App/Views/ProfileView+TV.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// ProfileView+TV.swift -// Passepartout -// -// Created by Davide De Rosa on 12/22/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import 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) { - 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: " ") - } -} diff --git a/Passepartout/App/Views/ProfileView+VPN.swift b/Passepartout/App/Views/ProfileView+VPN.swift deleted file mode 100644 index 01c67fb2..00000000 --- a/Passepartout/App/Views/ProfileView+VPN.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// ProfileView+VPN.swift -// Passepartout -// -// Created by Davide De Rosa on 3/18/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension ProfileView { - struct VPNSection: View { - @ObservedObject private var profileManager: ProfileManager - - private let profile: Profile - - @Binding private var modalType: ModalType? - - init(profile: Profile, modalType: Binding) { - profileManager = .shared - self.profile = profile - _modalType = modalType - } - - var body: some View { - Section { - toggleView - statusView - } header: { - Text(Unlocalized.VPN.vpn) - } footer: { - Text(L10n.Profile.Sections.Vpn.footer) - .xxxThemeTruncation() - } - } - } -} - -// MARK: - - -private extension ProfileView.VPNSection { - var interactiveProfile: Binding { - .init { - modalType == .interactiveAccount ? profile : nil - } set: { - modalType = $0 != nil ? .interactiveAccount : nil - } - } - - var isActiveProfile: Bool { - profileManager.isActiveProfile(profile.id) - } - - var toggleView: some View { - VPNToggle( - profile: profile, - interactiveProfile: interactiveProfile, - title: L10n.Global.Strings.enabled, - rateLimit: Constants.RateLimit.vpnToggle - ) - } - - var statusView: some View { - HStack { - Text(L10n.Profile.Items.ConnectionStatus.caption) - Spacer() - VPNStatusText(isActiveProfile: isActiveProfile) - .themeSecondaryTextStyle() - } - } -} diff --git a/Passepartout/App/Views/ProfileView.swift b/Passepartout/App/Views/ProfileView.swift deleted file mode 100644 index a2c69f6b..00000000 --- a/Passepartout/App/Views/ProfileView.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// ProfileView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/6/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct ProfileView: View { - enum ModalType: Int, Identifiable { - case interactiveAccount - - #if !os(tvOS) - case shortcuts - #endif - - case rename - - case paywallShortcuts - - case paywallNetworkSettings - - case paywallTrustedNetworks - - case paywallAppleTV - - var id: Int { - rawValue - } - } - - @ObservedObject private var currentProfile: ObservableProfile - - @State private var modalType: ModalType? - - init() { - currentProfile = ProfileManager.shared.currentProfile - } - - var body: some View { - debugChanges() - return Group { - if isLoading || isExisting { - mainView - } else { - WelcomeView() - } - }.toolbar { - ToolbarItemGroup(placement: .navigationBarTrailing) { - if themeIdiom != .phone { - SettingsButton() - } - MainMenu( - currentProfile: currentProfile, - modalType: $modalType - ).disabled(!isExisting) - } - }.sheet(item: $modalType, content: presentedModal) - .navigationTitle(title) - .themeSecondaryView() - } -} - -// MARK: - - -private extension ProfileView { - var isLoading: Bool { - currentProfile.isLoading - } - - var isExisting: Bool { - !currentProfile.value.isPlaceholder - } - - var title: String { - currentProfile.name - } - - var mainView: some View { - List { - if !isLoading { - VPNSection( - profile: currentProfile.value, - modalType: $modalType - ) - ProviderSection(currentProfile: currentProfile) - ConfigurationSection( - currentProfile: currentProfile, - modalType: $modalType - ) - TVSection( - currentProfile: currentProfile, - modalType: $modalType - ) - ExtraSection(currentProfile: currentProfile) - Section { - DiagnosticsRow(currentProfile: currentProfile) - } - } else { - ProgressView() - } - }.themeAnimation(on: isLoading) - } - - @ViewBuilder - func presentedModal(_ modalType: ModalType) -> some View { - switch modalType { - case .interactiveAccount: - NavigationView { - InteractiveConnectionView(profile: currentProfile.value) - }.themeGlobal() - - #if !os(tvOS) - case .shortcuts: - NavigationView { - ShortcutsView(target: currentProfile.value) - }.themeGlobal() - #endif - - case .rename: - NavigationView { - RenameView(currentProfile: currentProfile) - }.themeGlobal() - - case .paywallShortcuts: - NavigationView { - PaywallView( - modalType: $modalType, - feature: .siriShortcuts - ) - }.themeGlobal() - - case .paywallNetworkSettings: - NavigationView { - PaywallView( - modalType: $modalType, - feature: .networkSettings - ) - }.themeGlobal() - - case .paywallTrustedNetworks: - NavigationView { - PaywallView( - modalType: $modalType, - feature: .trustedNetworks - ) - }.themeGlobal() - - case .paywallAppleTV: - NavigationView { - PaywallView( - modalType: $modalType, - feature: .appleTV - ) - }.themeGlobal() - } - } -} diff --git a/Passepartout/App/Views/ProviderLocationView.swift b/Passepartout/App/Views/ProviderLocationView.swift deleted file mode 100644 index c10b43ec..00000000 --- a/Passepartout/App/Views/ProviderLocationView.swift +++ /dev/null @@ -1,339 +0,0 @@ -// -// ProviderLocationView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct ProviderLocationView: View, ProviderProfileAvailability { - @ObservedObject var providerManager: ProviderManager - - @ObservedObject private var currentProfile: ObservableProfile - - private let isEditable: Bool - - @Binding private var selectedServer: ProviderServer? - - @Binding private var favoriteLocationIds: Set? - - @AppStorage(AppPreference.isShowingFavorites.key) private var isShowingFavorites = false - - var profile: Profile { - currentProfile.value - } - - init(currentProfile: ObservableProfile, isEditable: Bool, isPresented: Binding) { - let providerManager: ProviderManager = .shared - - self.providerManager = providerManager - self.currentProfile = currentProfile - self.isEditable = isEditable - - _selectedServer = currentProfile.selectedServerBinding(providerManager: providerManager, isPresented: isPresented) - _favoriteLocationIds = currentProfile.providerFavoriteLocationIdsBinding - } - - var body: some View { - debugChanges() - return Group { - if isProviderProfileAvailable { - mainView - } else { - EmptyView() - } - }.toolbar { - Button { - withAnimation { - isShowingFavorites.toggle() - } - } label: { - themeFavoritesImage(isShowingFavorites).asSystemImage - } - }.navigationTitle(L10n.Provider.Location.title) - } -} - -extension ProviderLocationView { - struct LocationRow: View { - let location: ProviderLocation - - let selectedLocationId: String? - - var body: some View { - HStack { - themeAssetsCountryImage(location.countryCode).asAssetImage - VStack { - if let singleServer = location.onlyServer, - let shortServerDescription = singleServer.localizedDescription(optionalStyle: .short) { - - Text(location.localizedDescription(style: .country)) - .frame(maxWidth: .infinity, alignment: .leading) - Text(shortServerDescription) - .frame(maxWidth: .infinity, alignment: .leading) - } else { - Text(location.localizedDescription(style: .country)) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) - } - }.withTrailingCheckmark(when: location.id == selectedLocationId) - }.frame(height: 60) - } - } - - struct ServerListView: View { - @ObservedObject private var providerManager: ProviderManager - - private let location: ProviderLocation - - @Binding private var selectedServer: ProviderServer? - - init(location: ProviderLocation, selectedServer: Binding) { - providerManager = .shared - self.location = location - _selectedServer = selectedServer - } - - var body: some View { - ScrollViewReader { scrollProxy in - List { - ForEach(servers) { server in - Button(server.localizedDescription(style: .shortWithDefault)) { - selectedServer = server - }.withTrailingCheckmark(when: server.id == selectedServer?.id) - } - }.onAppear { - scrollToSelectedServer(scrollProxy) - } - } - } - } -} - -// MARK: - - -private extension ProviderLocationView { - var providerName: ProviderName { - guard let name = currentProfile.value.header.providerName else { - assertionFailure("Not a provider") - return "" - } - return name - } - - var vpnProtocol: VPNProtocolType { - currentProfile.value.currentVPNProtocol - } - - var mainView: some View { - // FIXME: layout, restore ScrollViewReader, but content inside it is not re-rendered on isShowingFavorites -// ScrollViewReader { scrollProxy in - List { - if !isShowingEmptyFavorites { - categoriesView - } else { - emptyFavoritesSection - } -// }.onAppear { -// scrollToSelectedLocation(scrollProxy) - } -// } - } - - var categoriesView: some View { - ForEach(categories, content: categorySection) - } - - func categorySection(_ category: ProviderCategory) -> some View { - Section { - ForEach(filteredLocations(for: category)) { location in - if isEditable { - locationRow(location) - #if !os(tvOS) - .swipeActions(edge: .trailing, allowsFullSwipe: false) { - favoriteActions(location) - } - #endif - } else { - locationRow(location) - } - } - } header: { - !category.name.isEmpty ? Text(category.name) : nil - } - } - - @ViewBuilder - func locationRow(_ location: ProviderLocation) -> some View { - if let onlyServer = location.onlyServer { - singleServerRow(location, onlyServer) - } else if profile.providerRandomizesServer ?? false { - singleServerRow(location, nil) - } else { - multipleServersRow(location) - } - } - - func multipleServersRow(_ location: ProviderLocation) -> some View { - NavigationLink(destination: { - ServerListView( - location: location, - selectedServer: $selectedServer - ).navigationTitle(location.localizedDescription(style: .country)) - }, label: { - LocationRow( - location: location, - selectedLocationId: selectedServer?.locationId - ) - }) - } - - func singleServerRow(_ location: ProviderLocation, _ server: ProviderServer?) -> some View { - Button { - selectedServer = server ?? location.servers?.randomElement() - } label: { - LocationRow( - location: location, - selectedLocationId: selectedServer?.locationId - ) - } - } - - func favoriteActions(_ location: ProviderLocation) -> some View { - Button { - withAnimation { - toggleFavoriteLocation(location) - } - } label: { - themeFavoriteActionImage(!isFavoriteLocation(location)).asSystemImage - }.themePrimaryTintStyle() - } - - var emptyFavoritesSection: some View { - Section { - } footer: { - Text(L10n.Provider.Location.Sections.EmptyFavorites.footer) - } - } - - var isShowingEmptyFavorites: Bool { - guard isShowingFavorites else { - return false - } - return favoriteLocationIds?.isEmpty ?? true - } -} - -private extension ProviderLocationView { - func server(withId serverId: String) -> ProviderServer? { - providerManager.server(withId: serverId) - } - - var categories: [ProviderCategory] { - providerManager.categories(providerName, vpnProtocol: vpnProtocol) - .filter { - !filteredLocations(for: $0).isEmpty - }.sorted() - } - - func filteredLocations(for category: ProviderCategory) -> [ProviderLocation] { - let locations: [ProviderLocation] - if isShowingFavorites { - locations = category.locations.filter { - favoriteLocationIds?.contains($0.id) ?? false - } - } else { - locations = category.locations - } - return locations.sorted() - } - - func isFavoriteLocation(_ location: ProviderLocation) -> Bool { - favoriteLocationIds?.contains(location.id) ?? false - } -} - -private extension ProviderLocationView.ServerListView { - var servers: [ProviderServer] { - providerManager.servers(forLocation: location).sorted() - } -} - -// MARK: - - -private extension ProviderLocationView { - func toggleFavoriteLocation(_ location: ProviderLocation) { - if !isFavoriteLocation(location) { - if favoriteLocationIds == nil { - favoriteLocationIds = [location.id] - } else { - favoriteLocationIds?.insert(location.id) - } - } else { - favoriteLocationIds?.remove(location.id) - } - // may trigger view updates? -// pp_log.debug("New favorite locations: \(favoriteLocationIds ?? [])") - } -} - -private extension ProviderLocationView { - func scrollToSelectedLocation(_ proxy: ScrollViewProxy) { - proxy.maybeScrollTo(selectedServer?.locationId) - } -} - -private extension ProviderLocationView.ServerListView { - func scrollToSelectedServer(_ proxy: ScrollViewProxy) { - proxy.maybeScrollTo(selectedServer?.id) - } -} - -// MARK: - Bindings - -private extension ObservableProfile { - - @MainActor - func selectedServerBinding(providerManager: ProviderManager, isPresented: Binding) -> Binding { - .init { - guard let serverId = self.value.providerServerId else { - return nil - } - return providerManager.server(withId: serverId) - } set: { - // user never selects a nil server - guard let server = $0 else { - return - } - self.value.setProviderServer(server) - isPresented.wrappedValue = false - } - } - - var providerFavoriteLocationIdsBinding: Binding?> { - .init { - self.value.providerFavoriteLocationIds - } set: { - self.value.providerFavoriteLocationIds = $0 - } - } -} diff --git a/Passepartout/App/Views/ProviderPresetView.swift b/Passepartout/App/Views/ProviderPresetView.swift deleted file mode 100644 index 23c41238..00000000 --- a/Passepartout/App/Views/ProviderPresetView.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// ProviderPresetView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct ProviderPresetView: View { - @Environment(\.presentationMode) private var presentationMode - - @ObservedObject private var providerManager: ProviderManager - - @ObservedObject private var currentProfile: ObservableProfile - - private var server: ProviderServer? - - @Binding private var selectedPreset: ProviderServer.Preset? - - init(currentProfile: ObservableProfile) { - let providerManager: ProviderManager = .shared - - self.providerManager = providerManager - self.currentProfile = currentProfile - - server = currentProfile.value.providerServer(providerManager) - _selectedPreset = currentProfile.selectedPresetBinding(providerManager: providerManager) - } - - var body: some View { - List { - ForEach(availablePresets, id: \.id, content: presetSection) - }.navigationTitle(L10n.Provider.Preset.title) - } -} - -// MARK: - - -private extension ProviderPresetView { - func presetSection(_ preset: ProviderServer.Preset) -> some View { - Section { - Button { - selectedPreset = preset - presentationMode.wrappedValue.dismiss() - } label: { - presetSelectionRow(preset) - } - NavigationLink(L10n.Endpoint.Advanced.title) { - - // TODO: WireGuard, preset assumes OpenVPN (read current protocol instead) - preset.openVPNConfiguration.map { - EndpointAdvancedView.OpenVPNView( - builder: .constant($0.builder()), - isReadonly: true, - isServerPushed: false - ).navigationTitle(preset.name) - } - } - } header: { - Text(preset.name) - } - } - - func presetSelectionRow(_ preset: ProviderServer.Preset) -> some View { - Text(preset.comment) - .withTrailingCheckmark(when: preset.id == selectedPreset?.id) - } - - // some providers (e.g. NordVPN) have specific presets based on selected server - var availablePresets: [ProviderServer.Preset] { - server?.presets?.sorted() ?? [] - } -} - -// MARK: - Bindings - -private extension ObservableProfile { - - @MainActor - func selectedPresetBinding(providerManager: ProviderManager) -> Binding { - .init { - guard let serverId = self.value.providerServerId else { - return nil - } - guard let server = providerManager.server(withId: serverId) else { - return nil - } - return self.value.providerPreset(server) - } set: { - // user never selects a nil preset - guard let preset = $0 else { - return - } - self.value.setProviderPreset(preset) - } - } -} diff --git a/Passepartout/App/Views/ReportIssueView.swift b/Passepartout/App/Views/ReportIssueView.swift deleted file mode 100644 index 2b6f5643..00000000 --- a/Passepartout/App/Views/ReportIssueView.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// ReportIssueView.swift -// Passepartout -// -// Created by Davide De Rosa on 3/24/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import MessageUI -import PassepartoutLibrary -import SwiftUI - -struct ReportIssueView: View { - @Binding private var isPresented: Bool - - private let toRecipients: [String] - - private let subject: String - - private let messageBody: String - - private let logs: [MailComposerView.Attachment] - - init( - isPresented: Binding, - vpnProtocol: VPNProtocolType, - messageBody: String, - logs: [MailComposerView.Attachment] - ) { - _isPresented = isPresented - - toRecipients = [Unlocalized.Issues.recipient] - subject = Unlocalized.Issues.subject - self.messageBody = messageBody - self.logs = logs - } - - var body: some View { - MailComposerView( - isPresented: $isPresented, - toRecipients: toRecipients, - subject: subject, - messageBody: messageBody, - attachments: logs - ) - } -} -#endif diff --git a/Passepartout/App/Views/SettingsView.swift b/Passepartout/App/Views/SettingsView.swift deleted file mode 100644 index 12e3819e..00000000 --- a/Passepartout/App/Views/SettingsView.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// SettingsView.swift -// Passepartout -// -// Created by Davide De Rosa on 8/19/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct SettingsView: View { - @ObservedObject private var profileManager: ProfileManager - - @ObservedObject private var productManager: ProductManager - - @ObservedObject private var persistenceManager: PersistenceManager - - @Environment(\.presentationMode) private var presentationMode - - @AppStorage(AppPreference.locksInBackground.key) private var locksInBackground = false - - private let versionString = Constants.Global.appVersionString - - init() { - profileManager = .shared - productManager = .shared - persistenceManager = .shared - } - - var body: some View { - List { - #if !targetEnvironment(macCatalyst) - preferencesSection - #endif - iCloudSection - diagnosticsSection - aboutSection - }.toolbar { - themeCloseItem(presentationMode: presentationMode) - }.themeSecondaryView() - .navigationTitle(L10n.Settings.title) - } -} - -// MARK: - - -private extension SettingsView { - var preferencesSection: some View { - Section { - Toggle(L10n.Settings.Items.LocksInBackground.caption, isOn: $locksInBackground) - } header: { - Text(L10n.Preferences.title) - } - } - - var iCloudSection: some View { - Section { - Toggle(L10n.Settings.Items.ShouldEnableCloudSyncing.caption, isOn: $persistenceManager.shouldEnableCloudSyncing.themeAnimation()) - Button(L10n.Settings.Items.EraseCloudStore.caption) { - Task { - await persistenceManager.eraseCloudKitStore() - } - }.withTrailingProgress(when: persistenceManager.isErasingCloudKitStore) - .disabled(persistenceManager.shouldEnableCloudSyncing || persistenceManager.isErasingCloudKitStore) - } header: { - Text(Unlocalized.Other.iCloud) - } footer: { - Text(L10n.Settings.Sections.Icloud.footer) - } - } - - var diagnosticsSection: some View { - Section { - DiagnosticsRow(currentProfile: profileManager.currentProfile) - } - } - - var aboutSection: some View { - Section { - NavigationLink { - DonateView() - } label: { - Text(L10n.Settings.Items.Donate.caption) - }.disabled(!productManager.canMakePayments()) - NavigationLink { - AboutView() - } label: { - Text(L10n.About.title) - } - } footer: { - HStack { - Spacer() - Text(versionString) - Spacer() - } - } - } -} diff --git a/Passepartout/App/Views/ShortcutsView+Add.swift b/Passepartout/App/Views/ShortcutsView+Add.swift deleted file mode 100644 index 4109f834..00000000 --- a/Passepartout/App/Views/ShortcutsView+Add.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// ShortcutsView+Add.swift -// Passepartout -// -// Created by Davide De Rosa on 3/13/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Intents -import PassepartoutLibrary -import SwiftUI - -extension ShortcutsView { - struct AddView: View { - @ObservedObject private var providerManager: ProviderManager - - @StateObject private var pendingProfile = ObservableProfile() - - private let target: Profile - - @Binding private var pendingShortcut: INShortcut? - - @State private var isPresentingProviderLocation = false - - init(target: Profile, pendingShortcut: Binding) { - providerManager = .shared - self.target = target - _pendingShortcut = pendingShortcut - } - - var body: some View { - ZStack { - hiddenProviderLocationLink - List { - Section { - addConnectView - Button(L10n.Shortcuts.Add.Items.EnableVpn.caption, action: addEnableVPN) - Button(L10n.Shortcuts.Add.Items.DisableVpn.caption, action: addDisableVPN) - } header: { - Text(Unlocalized.VPN.vpn) - } - Section { - Button(L10n.Shortcuts.Add.Items.TrustCurrentWifi.caption, action: addTrustWiFi) - Button(L10n.Shortcuts.Add.Items.UntrustCurrentWifi.caption, action: addUntrustWiFi) - } header: { - Text(L10n.Shortcuts.Add.Sections.Wifi.header) - } - Section { - Button(L10n.Shortcuts.Add.Items.TrustCellular.caption, action: addTrustCellular) - Button(L10n.Shortcuts.Add.Items.UntrustCellular.caption, action: addUntrustCellular) - } header: { - Text(L10n.Shortcuts.Add.Sections.Cellular.header) - } - } - }.navigationTitle(L10n.Shortcuts.Add.title) - } - } -} - -// MARK: - - -private extension ShortcutsView.AddView { - var addConnectView: some View { - Button(L10n.Shortcuts.Add.Items.Connect.caption) { - if target.isProvider { - pendingProfile.value = target - isPresentingProviderLocation = true - } else { - addConnect(target.header) - } - } - } - - var hiddenProviderLocationLink: some View { - NavigationLink("", isActive: $isPresentingProviderLocation) { - ProviderLocationView( - currentProfile: pendingProfile, - isEditable: false, - isPresented: isProviderLocationPresented - ) - } - } - - var isProviderLocationPresented: Binding { - .init { - isPresentingProviderLocation - } set: { - if !$0 { - isPresentingProviderLocation = false - addMoveToPendingProfile() - } - } - } -} - -// MARK: - - -private extension ShortcutsView.AddView { - func addConnect(_ header: Profile.Header) { - pendingShortcut = INShortcut(intent: IntentDispatcher.intentConnect( - header: header - )) - } - - func addMoveToPendingProfile() { - let header = pendingProfile.value.header - guard let server = pendingProfile.value.providerServer(providerManager) else { - return - } - pendingShortcut = INShortcut(intent: IntentDispatcher.intentMoveTo( - header: header, - providerFullName: server.providerMetadata.fullName, - server: server - )) - } - - func addEnableVPN() { - addShortcut(with: IntentDispatcher.intentEnable()) - } - - func addDisableVPN() { - addShortcut(with: IntentDispatcher.intentDisable()) - } - - func addTrustWiFi() { - addShortcut(with: IntentDispatcher.intentTrustWiFi()) - } - - func addUntrustWiFi() { - addShortcut(with: IntentDispatcher.intentUntrustWiFi()) - } - - func addTrustCellular() { - addShortcut(with: IntentDispatcher.intentTrustCellular()) - } - - func addUntrustCellular() { - addShortcut(with: IntentDispatcher.intentUntrustCellular()) - } - - func addShortcut(with intent: INIntent) { - guard let shortcut = INShortcut(intent: intent) else { - fatalError("Unable to create INShortcut, intent '\(intent.description)' not exposed by app?") - } - pendingShortcut = shortcut - } -} -#endif diff --git a/Passepartout/App/Views/ShortcutsView.swift b/Passepartout/App/Views/ShortcutsView.swift deleted file mode 100644 index 43bb669e..00000000 --- a/Passepartout/App/Views/ShortcutsView.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// ShortcutsView.swift -// Passepartout -// -// Created by Davide De Rosa on 2/8/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if !os(tvOS) -import Intents -import PassepartoutLibrary -import SwiftUI - -struct ShortcutsView: View { - enum ModalType: Identifiable { - case edit(shortcut: Shortcut) - - case add(shortcut: INShortcut) - - // XXX: alert ids - var id: Int { - switch self { - case .edit: return 1 - - case .add: return 2 - } - } - } - - @StateObject private var intentsManager = IntentsManager() - - @Environment(\.presentationMode) private var presentationMode - - private let target: Profile - - @State private var modalType: ModalType? - - @State private var isNavigationPresented = false - - @State private var pendingShortcut: INShortcut? - - init(target: Profile) { - self.target = target - } - - var body: some View { - List { - if !intentsManager.shortcuts.isEmpty { - shortcutsSection - } - addSection - }.toolbar { - themeCloseItem(presentationMode: presentationMode) - }.sheet(item: $modalType, content: presentedModal) - .themeAnimation(on: intentsManager.isReloadingShortcuts) - - // IntentsUI - .onReceive(intentsManager.shouldDismissIntentView) { _ in - modalType = nil - } - - .navigationTitle(Unlocalized.Other.siri) - .themeSecondaryView() - } -} - -// MARK: - - -private extension ShortcutsView { - var shortcutsSection: some View { - Section { - ForEach(relevantShortcuts, content: rowView) - } header: { - Text(L10n.Shortcuts.Edit.Sections.All.header) - } - } - - var relevantShortcuts: [Shortcut] { - intentsManager.shortcuts.values.filter { - $0.isRelevant(to: target) - }.sorted() - } - - var addSection: some View { - Section { - NavigationLink(isActive: $isNavigationPresented) { - AddView( - target: target, - pendingShortcut: delegatingPendingShortcut - ) - } label: { - Text(L10n.Shortcuts.Edit.Items.AddShortcut.caption) - } - } footer: { - Text(L10n.Shortcuts.Edit.Sections.Add.footer) - } - } - - @ViewBuilder - func presentedModal(_ modalType: ModalType) -> some View { - switch modalType { - case .edit(let shortcut): - IntentEditView( - shortcut: shortcut, - delegate: intentsManager - ) - - case .add(let shortcut): - IntentAddView( - shortcut: shortcut, - delegate: intentsManager - ) - } - } - - func rowView(forShortcut vs: Shortcut) -> some View { - Button { - presentEditShortcut(vs) - } label: { - Label(vs.native.invocationPhrase, systemImage: themeShortcutsImage) - } - } - - var delegatingPendingShortcut: Binding { - .init { - pendingShortcut - } set: { - guard let pendingShortcut = $0 else { - return - } - presentAddShortcut(pendingShortcut) - } - } -} - -private extension Shortcut { - func isRelevant(to profile: Profile) -> Bool { - guard let intent = native.shortcut.intent else { - return true - } - if let connectIntent = intent as? ConnectVPNIntent { - return connectIntent.profileId == profile.id.uuidString - } - if let moveToIntent = intent as? MoveToLocationIntent { - return moveToIntent.profileId == profile.id.uuidString - } - return true - } -} - -// MARK: - - -private extension ShortcutsView { - func presentEditShortcut(_ shortcut: Shortcut) { - modalType = .edit(shortcut: shortcut) - } - - func presentAddShortcut(_ shortcut: INShortcut) { - isNavigationPresented = false - modalType = .add(shortcut: shortcut) - } -} -#endif diff --git a/Passepartout/App/Views/TV/ActiveProfileView+TV.swift b/Passepartout/App/Views/TV/ActiveProfileView+TV.swift deleted file mode 100644 index 9f8a90e4..00000000 --- a/Passepartout/App/Views/TV/ActiveProfileView+TV.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// ActiveProfileView+TV.swift -// Passepartout -// -// Created by Davide De Rosa on 12/18/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if os(tvOS) -import 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 diff --git a/Passepartout/App/Views/TV/OrganizerView+TV.swift b/Passepartout/App/Views/TV/OrganizerView+TV.swift deleted file mode 100644 index 020a7bb7..00000000 --- a/Passepartout/App/Views/TV/OrganizerView+TV.swift +++ /dev/null @@ -1,177 +0,0 @@ -// -// OrganizerView+TV.swift -// Passepartout -// -// Created by Davide De Rosa on 12/17/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if os(tvOS) -import 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 - deleteSection - } - .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) - } - } - - var deleteSection: some View { - Section { - Button(L10n.Global.Strings.delete, role: .destructive) { - profileManager.activeProfileId.map { profileId in - withAnimation { - profileManager.removeProfiles(withIds: [profileId]) - } - } - } - .disabled(!profileManager.hasActiveProfile) - } - } -} - -// 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 diff --git a/Passepartout/App/Views/TV/ProfilesList+TV.swift b/Passepartout/App/Views/TV/ProfilesList+TV.swift deleted file mode 100644 index 3fcb95a0..00000000 --- a/Passepartout/App/Views/TV/ProfilesList+TV.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// ProfilesList+TV.swift -// Passepartout -// -// Created by Davide De Rosa on 12/18/23. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -#if os(tvOS) -import 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 diff --git a/Passepartout/App/Views/VPNToggle.swift b/Passepartout/App/Views/VPNToggle.swift deleted file mode 100644 index 8a67c2e9..00000000 --- a/Passepartout/App/Views/VPNToggle.swift +++ /dev/null @@ -1,145 +0,0 @@ -// -// VPNToggle.swift -// Passepartout -// -// Created by Davide De Rosa on 4/26/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -struct VPNToggle: View { - @ObservedObject private var profileManager: ProfileManager - - @ObservedObject private var vpnManager: VPNManager - - @ObservedObject private var currentVPNState: ObservableVPNState - - @ObservedObject private var productManager: ProductManager - - private let profile: Profile - - @Binding private var interactiveProfile: Profile? - - private let title: String - - private let rateLimit: Int - - @State private var canToggle = true - - init(profile: Profile, interactiveProfile: Binding, 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(title, isOn: isEnabled) - .disabled(!canToggle) - .themeAnimation(on: currentVPNState.isEnabled) - .tint(.accentColor) - } -} - -// MARK: - - -private extension VPNToggle { - var isEnabled: Binding { - .init { - isActiveProfile && currentVPNState.isEnabled && !shouldPromptForAccount - } set: { newValue in - guard !shouldPromptForAccount else { - interactiveProfile = profile - return - } - guard newValue else { - disableVPN() - return - } - enableVPN() - } - } - - var isActiveProfile: Bool { - profileManager.isActiveProfile(profile.id) - } - - var shouldPromptForAccount: Bool { - profile.account.authenticationMethod == .interactive && (currentVPNState.vpnStatus == .disconnecting || currentVPNState.vpnStatus == .disconnected) - } - - var isEligibleForSiri: Bool { - productManager.isEligible(forFeature: .siriShortcuts) - } -} - -// MARK: - - -private extension VPNToggle { - func enableVPN() { - Task { @MainActor in - canToggle = false - await Task.maybeWait(forMilliseconds: rateLimit) - canToggle = true - do { - let profile = try await vpnManager.connect(with: profile.id) - donateIntents(withProfile: profile) - } catch { - pp_log.warning("Unable to connect to profile \(profile.id): \(error)") - canToggle = true - - ErrorHandler.shared.handle(error, title: profile.header.name) - } - } - } - - func disableVPN() { - Task { @MainActor in - canToggle = false - await vpnManager.disable() - canToggle = true - } - } - - func donateIntents(withProfile profile: Profile) { - - // eligibility: donate intents if eligible for Siri - guard isEligibleForSiri else { - return - } - - pp_log.debug("Donating connection intents...") - - #if !os(tvOS) - IntentDispatcher.donateEnableVPN() - IntentDispatcher.donateDisableVPN() - IntentDispatcher.donateConnection( - with: profile, - providerManager: ProviderManager.shared - ) - #endif - } -} diff --git a/Passepartout/App/Views/View+Extensions.swift b/Passepartout/App/Views/View+Extensions.swift deleted file mode 100644 index a98088ff..00000000 --- a/Passepartout/App/Views/View+Extensions.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// View+Extensions.swift -// Passepartout -// -// Created by Davide De Rosa on 2/18/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import PassepartoutLibrary -import SwiftUI - -extension View { - func withoutTitleBar() -> some View { - #if targetEnvironment(macCatalyst) - withHostingWindow { window in - guard let titlebar = window?.windowScene?.titlebar else { - return - } - titlebar.titleVisibility = .hidden - titlebar.toolbar = nil - } - #else - self - #endif - } - - func withLeadingText(_ text: String?, color: Color? = nil, truncationMode: Text.TruncationMode = .tail) -> some View { - HStack { - text.map(Text.init) - .foregroundColor(color) - .lineLimit(1) - .truncationMode(truncationMode) - Spacer() - self - } - } - - func withLeadingLabel(_ text: String, color: Color? = nil, image: String) -> some View { - HStack { - Label(text, image: image) - .foregroundColor(color) - Spacer() - self - } - } - - func withTrailingText(_ text: String?, truncationMode: Text.TruncationMode = .tail, copyOnTap: Bool = false) -> some View { - HStack { - self - Spacer() - let trailing = text.map(Text.init) - .themeSecondaryTextStyle() - .lineLimit(1) - .truncationMode(truncationMode) - if copyOnTap { - trailing - #if !os(tvOS) - .onTapGesture { - text.map(Utils.copyToPasteboard) - } - #endif - } else { - trailing - } - } - } - - func withTrailingCheckmark(when condition: Bool) -> some View { - HStack { - self - if condition { - Spacer() - themeCheckmarkImage.asSystemImage - .themeAccentForegroundStyle() - } - } - } - - func withTrailingProgress(when condition: Bool) -> some View { - HStack { - self - .disabled(condition) - if condition { - Spacer() - ProgressView() - } - } - } -} - -extension View { - func debugChanges() { - if Passepartout.shared.logger.logLevel == .verbose { - Self._printChanges() - } - } -} - -extension ScrollViewProxy { - func maybeScrollTo( - _ id: ID?, - afterMilliseconds millis: Int = Constants.Delays.scrolling, - animated: Bool = false, - anchor: UnitPoint = .center - ) { - guard let id = id else { - return - } - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(millis)) { - if animated { - withAnimation { - scrollTo(id, anchor: anchor) - } - } else { - scrollTo(id, anchor: anchor) - } - } - } -} - -// https://stackoverflow.com/questions/65238068/hide-title-bar-in-swiftui-app-for-maccatalyst - -private extension View { - func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View { - background(HostingWindowFinder(callback: callback)) - } -} - -private struct HostingWindowFinder: UIViewRepresentable { - var callback: (UIWindow?) -> Void - - func makeUIView(context: Context) -> UIView { - let view = UIView() - DispatchQueue.main.async { [weak view] in - self.callback(view?.window) - } - return view - } - - func updateUIView(_ uiView: UIView, context: Context) { - } -} diff --git a/Passepartout/App/de.lproj/InfoPlist.strings b/Passepartout/App/de.lproj/InfoPlist.strings deleted file mode 100644 index e1e2deaf..00000000 --- a/Passepartout/App/de.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Zugangsname des aktuellen WLAN"; - -"NSFaceIDUsageDescription" = "App mit Face ID entsperren"; diff --git a/Passepartout/App/de.lproj/Localizable.strings b/Passepartout/App/de.lproj/Localizable.strings deleted file mode 100644 index 8814e8ab..00000000 --- a/Passepartout/App/de.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Richtlinie"; -"global.strings.networks" = "Netzwerke"; -"global.strings.edit" = "Bearbeiten"; -"global.strings.unknown" = "Unbekannt"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Bitte einen Endpunkt"; - -"on_demand.sections.policy.footer.any" = "in einem beliebigen Netzwerk auswählen"; -"on_demand.sections.policy.footer.including" = "innerhalb der unten stehenden Netzwerke auswählen"; -"on_demand.sections.policy.footer.excluding" = "außerhalb der unten stehenden Netzwerke auswählen"; -"on_demand.policy.any" = "Alle Netzwerke"; -"on_demand.policy.including" = "Einschließlich"; -"on_demand.policy.excluding" = "Ausschließlich"; - -"settings.sections.icloud.footer" = "Deaktiviere zum Löschen die Synchronisierung. Tue dies auf allen synchronisierten Geräten, um den iCloud-Store sicher zu löschen. Lokale Profile sind davon nicht betroffen."; -"settings.items.should_enable_cloud_syncing.caption" = "Mit iCloud synchronisieren"; -"settings.items.erase_cloud_store.caption" = "iCloud-Store löschen"; - -/* MARK: Global */ - -"global.strings.cancel" = "Abbrechen"; -"global.strings.next" = "Weiter"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Speichern"; -"global.strings.rename" = "Umbenennen"; -"global.strings.duplicate" = "Duplizieren"; -"global.strings.add" = "Hinzufügen"; -"global.strings.delete" = "Löschen"; -"global.strings.uninstall" = "Deinstallieren"; -"global.strings.default" = "Default"; -"global.strings.name" = "Name"; -"global.strings.profiles" = "Profile"; -"global.strings.provider" = "Anbieter"; -"global.strings.providers" = "Anbieter"; -"global.strings.configuration" = "Konfiguration"; -"global.strings.address" = "Adresse"; -"global.strings.addresses" = "Adressen"; -"global.strings.port" = "Port"; -"global.strings.protocol" = "Protokoll"; -"global.strings.protocols" = "Protokolle"; -"global.strings.enabled" = "Aktiviert"; -"global.strings.disabled" = "Deaktiviert"; -"global.strings.none" = "Keine"; -"global.strings.automatic" = "Automatisch"; -"global.strings.manual" = "Manuell"; -"global.strings.encryption" = "Verschlüsselung"; -"global.strings.reconnect" = "Erneut verbinden"; -"global.strings.servers" = "Server"; -"global.strings.domain" = "Domäne"; -"global.strings.domains" = "Domänen"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interface"; -"global.strings.private_key" = "Privater Key"; -"global.strings.public_key" = "Öffentlicher Key"; -"global.strings.endpoint" = "Endpunkt"; -"global.strings.keepalive" = "Keep-alive"; -"global.strings.advanced" = "Erweitert"; -"global.strings.translations" = "Übersetzungen"; -"global.strings.show" = "Anzeigen"; -"global.strings.connect" = "Verbinden"; -"global.strings.disconnect" = "Trennen"; -"global.strings.download" = "Herunterladen"; -"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"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Später erinnern"; -"global.alerts.buttons.never" = "Nicht erneut fragen"; - -"global.placeholders.profile_name" = "Mein Profil"; - -"global.errors.missing_profile" = "Fehlendes Profil"; -"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 */ - -"menu.all.support.title" = "Support"; -"menu.all.share.title" = "Teilen"; -"menu.all.about.title" = "Über %@"; - -"menu.contextual.add_profile.from_files" = "Aus Dateien"; -"menu.contextual.add_profile.from_text" = "Aus Text"; -"menu.contextual.add_profile.imported" = "%@ hinzufügen"; -"menu.contextual.support.write_review" = "Bewerten"; -"menu.contextual.support.join_community" = "Community"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Einladen"; - -"menu.system.quit.title" = "%@ beenden"; - -"menu.system.quit.messages.confirm" = "Wenn das VPN aktiviert wurde, läuft es weiter im Hintergrund. Möchtest du beenden?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Verbinde"; -"tunnelkit.vpn.active" = "Aktiv"; -"tunnelkit.vpn.disconnecting" = "Trenne"; -"tunnelkit.vpn.inactive" = "Inaktiv"; -"tunnelkit.vpn.disabled" = "Deaktiviert"; -"tunnelkit.vpn.unused" = "Aus"; - -"tunnelkit.errors.vpn.timeout" = "Timeout"; -"tunnelkit.errors.vpn.dns" = "DNS fehlgeschlagen"; -"tunnelkit.errors.vpn.auth" = "Authentifizierung fehlgeschlagen"; -"tunnelkit.errors.vpn.tls" = "TLS fehlgeschlagen"; -"tunnelkit.errors.vpn.encryption" = "Verschlüsselung fehlgeschlagen"; -"tunnelkit.errors.vpn.compression" = "Komprimierung nicht unterstützt"; -"tunnelkit.errors.vpn.network" = "Netzwerk geändert"; -"tunnelkit.errors.vpn.routing" = "Kein Routing"; -"tunnelkit.errors.vpn.gateway" = "Kein Gateway"; -"tunnelkit.errors.vpn.shutdown" = "Server heruntergefahren"; - -"tunnelkit.errors.parsing" = "Fehler beim Verarbeiten der Konfigurationsdatei (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Die Konfigurations-Datei enthält eine ungültige Option (%@)."; -"tunnelkit.errors.openvpn.required_option" = "Die Konfigurations-Datei enthält eine benötigte Option nicht (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Die Konfigurations-Datei enthält eine nicht unterstützte Option (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Die Konfigurations-Datei ist korrekt, enthält aber möglicherweise eine nicht unterstützte Option (%@).\n\nDie Verbindung kann, abhängig von den Server-Einstellungen, unterbrochen werden."; -"tunnelkit.errors.openvpn.passphrase_required" = "Bitte die Verschlüsselungs-Passphrase eingeben."; -"tunnelkit.errors.openvpn.decryption" = "Die Konfiguration enthält einen verschlüsselten Private Key und konnte nicht entschlüsselt werden. Bitte überprüfe ob du die Passphrase eingegeben hast."; - -/* 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"; - -"organizer.alerts.reddit.message" = "Wusstest du, daß Passepartout einen Subreddit hat? Abonniere ihn für Updates oder um Features, Probleme, neue Plattformen zu diskutieren - oder was auch immer du möchtest.\n\nDies ist auch ein guter Weg zu zeigen dass dir dieses Projekt etwas bedeutet."; -"organizer.alerts.reddit.buttons.subscribe" = "Jetzt abbonnieren!"; - -"organizer.alerts.remove_profile.message" = "Bist du sicher, dass du das Profil %@ löschen möchtest?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Neues Profil"; -"add_profile.shared.views.existing.header" = "Bestehende Profile"; -"add_profile.shared.alerts.overwrite.message" = "Ein Profil mit identischem Namen existiert bereits. Ersetzen?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Passphrase eingeben"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Hier findest du einige Anbieter mit voreingestellten Konfigurationsprofilen."; -"add_profile.provider.items.update_list" = "Aktualisiere Liste"; -"add_profile.provider.errors.no_default_server" = "Keine Server gefunden."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Willkommen bei Passepartout!\n\nBenutze den Organizer um ein neues Profil hinzuzufügen."; - -"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"; -"profile.items.use_profile.caption" = "Dieses Profil verwenden"; -"profile.items.vpn_service.caption" = "Aktiviert"; -"profile.items.vpn.turn_on.caption" = "Aktiviere VPN"; -"profile.items.vpn.turn_off.caption" = "Deaktiviere VPN"; -"profile.items.connection_status.caption" = "Status"; -"profile.items.data_count.caption" = "Ausgetauschte Datenmenge"; -"profile.items.randomizes_server.caption" = "Server zufällig wählen"; -"profile.items.provider.refresh.caption" = "Infrastruktur neu laden"; -"profile.items.category.caption" = "Kategorie"; -"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?"; -"profile.alerts.uninstall_vpn.message" = "Möchtest du wirklich die VPN-Konfiguration aus deinen Geräte-Einstellungen löschen? Dies behebt möglicherweise manche kaputten VPN-Zustände und beeinflusst nicht deine Anbieter und Hosts-Profile."; -"profile.alerts.test_connectivity.title" = "Konnektivität"; -"profile.alerts.test_connectivity.messages.success" = "Dein Gerät ist mit dem Internet verbunden!"; -"profile.alerts.test_connectivity.messages.failure" = "Dein Gerät hat keine Verbindung mit dem Internet, bitte prüfe deine Profil-Parameter."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Ort"; -"provider.location.sections.empty_favorites.footer" = "Wische nach Links um einen Standort zu den Favoriten hinzuzufügen oder zu entfernen."; -"provider.location.actions.favorite" = "Favorit hinzuzufügen"; -"provider.location.actions.unfavorite" = "Favorit entfernen"; - -"provider.preset.title" = "Voreinstellung"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Account"; -"account.sections.credentials.header" = "Zugangsdaten"; -"account.sections.registration.footer" = "Beantrage einen Account auf der %@ Webseite."; -"account.items.authentication_method.persistent" = "Persistent"; -"account.items.authentication_method.interactive" = "Interaktiv"; -"account.items.username.caption" = "Benutzername"; -"account.items.username.placeholder" = "Benutzername"; -"account.items.password.caption" = "Passwort"; -"account.items.password.placeholder" = "Geheim"; -"account.items.seed.caption" = "Seed"; -"account.items.open_guide.caption" = "Siehe deine Zugangsdaten"; -"account.items.signup.caption" = "Registrieren bei %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Benutze deine %@ Web-Zugangsdaten."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Verwenden Sie Ihre %@ service-Anmeldeinformationen, die von den Website-Anmeldeinformationen abweichen können."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise numerischt (ohne Zwischenraum)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise deine Email."; -"account.sections.guidance.footer.infrastructure.pia" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise numerischt mit einem \"p\" Präfix."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Deine Zugangsdaten für %@ findest du unter \"Account > OpenVPN / IKEv2 Username\" auf der Webseite."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise deine Email."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Benutze deine %@ Web-Zugangsdaten. Dein Benutzername ist üblicherweise deine Email."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Deine Zugangsdaten für %@ findest du im OpenVPN Config Generator auf der Webseite."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Vorinstallierter Key"; -"endpoint.wireguard.items.allowed_ip.caption" = "Zulässige IP"; - -"endpoint.advanced.title" = "Technische Details"; -"endpoint.advanced.openvpn.sections.pull.header" = "Vom Server ziehen"; -"endpoint.advanced.openvpn.sections.communication.header" = "Kommunikation"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Wenn du nach einer Änderung der Kommunikations-Parameter dich nicht mehr verbinden kannst, hier tippen um zur originalen Konfiguration zurückzukehren."; -"endpoint.advanced.openvpn.sections.compression.header" = "Komprimierung"; -"endpoint.advanced.openvpn.sections.network.header" = "Netzwerk"; -"endpoint.advanced.openvpn.sections.other.header" = "Andere"; -"endpoint.advanced.openvpn.items.route.caption" = "Route"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Chiffre"; -"endpoint.advanced.openvpn.items.digest.caption" = "Authentifizierung"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Eingebettet"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algorithmus"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Nicht unterstützt"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Konfiguration zurücksetzen"; -"endpoint.advanced.openvpn.items.client.caption" = "Zertifikat"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Key"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Geprüft"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Nicht geprüft"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Authentifizierung"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Verschlüsselung"; -"endpoint.advanced.openvpn.items.eku.caption" = "Erweiterte Verifizierung"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d Sekunden"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "erneute Aushandlung"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "nach %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Endpunkt zufällig wählen"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Hostnamen zufällig wählen"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Netzwerk-Einstellungen"; -"network_settings.sections.choices.header" = "Überschreiben"; -"network_settings.gateway.title" = "Standard-Gateway"; -"network_settings.proxy.items.bypass_domains.caption" = "Domänen umgehen"; -"network_settings.items.add_dns_server.caption" = "Adresse hinzufügen"; -"network_settings.items.add_dns_domain.caption" = "Domäne hinzufügen"; -"network_settings.items.proxy_bypass.caption" = "Domäne umgehen"; -"network_settings.items.add_proxy_bypass.caption" = "Zu umgehende Domäne hinzufügen"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "On-demand"; -"on_demand.sections.policy.footer" = "VPN %@ aktivieren."; -"on_demand.items.add_ssid.caption" = "WLAN hinzufügen"; -"on_demand.items.active.caption" = "Vertrauen"; -"on_demand.items.mobile.caption" = "Mobilfunknetz"; -"on_demand.items.ethernet.caption" = "Kabelverbindungen"; - -"on_demand.items.policy.caption" = "Vertrauen deaktiviert VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnose"; -"diagnostics.sections.debug_log.footer" = "Zensier-Status wird aktiv nach erneutem Verbinden. Netzwerk-Daten sind Hostnamen, IP-Adressen, Routingtabellen, SSID. Zugangsdaten und Private Keys werden nie gelogged."; -"diagnostics.items.server_configuration.caption" = "Serverkonfiguration"; -"diagnostics.items.app_log.title" = "App"; -"diagnostics.items.masks_private_data.caption" = "Netzwerkdaten zensieren"; -"diagnostics.items.report_issue.caption" = "Verbindungsproblem melden"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Um das aktuelle Debug-Log sicher zurückzusetzen und die neuen Zensier-Paramenter anzuwenden, musst du das VPN jetzt erneut verbinden."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Debug log"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Kopieren"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Problem melden"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Füge Kurzbefehl hinzu"; -"shortcuts.add.sections.wifi.header" = "WLAN"; -"shortcuts.add.sections.cellular.header" = "Mobilfunknetz"; -"shortcuts.add.items.connect.caption" = "Verbinde mit"; -"shortcuts.add.items.enable_vpn.caption" = "Aktiviere VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Deaktiviere VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Vertraue aktivem WLAN"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Misstraue aktivem WLAN"; -"shortcuts.add.items.trust_cellular.caption" = "Vertraue Mobilfunknetz"; -"shortcuts.add.items.untrust_cellular.caption" = "Misstraue Mobilfunknetz"; -"shortcuts.add.alerts.no_profiles.message" = "Es gibt kein Profil mit dem eine Verbindung hergestellt werden kann."; - -"shortcuts.edit.title" = "Kurzbefehle bearbeiten"; -"shortcuts.edit.sections.all.header" = "Existierende Kurzbefehle"; -"shortcuts.edit.sections.add.footer" = "Erhalte Hilfe von Siri um deine üblichen Interaktionen mit der App zu beschleunigen."; -"shortcuts.edit.items.add_shortcut.caption" = "Kurzbefehl hinzufügen"; - -/* MARK: PaywallView */ - -"paywall.title" = "Kaufen"; -"paywall.sections.products.footer" = "Jedes Produkt ist ein einmaliger Kauf. Der Kauf eines Providers beinhaltet kein VPN-Abonnement."; -"paywall.items.loading.caption" = "Produkte werden geladen"; -"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 */ - -"donate.title" = "Spenden"; -"donate.sections.one_time.header" = "Einmalig"; -"donate.sections.one_time.footer" = "Wenn du dich erkenntlich zeigen möchtest für meine Arbeit, gibt es hier ein paar Beträge die du direkt spenden kannst.\n\nDu bezahlst pro Spende nur einmal und kannst mehrmals spenden wenn du möchtest."; -"donate.items.loading.caption" = "Lade Spenden"; -"donate.items.purchasing.caption" = "Führe Spende durch"; -"donate.alerts.purchase.success.message" = "Das bedeutet mir viel und ich hoffe wirklich dass du die App weiterhin benutzt und unterstützt."; -"donate.alerts.purchase.failure.message" = "Konnte Spende nicht durchführen. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Einstellungen"; -"settings.items.locks_in_background.caption" = "App-Zugriff sperren"; -"settings.items.donate.caption" = "Spenden"; - -/* MARK: AboutView */ - -"about.title" = "Über"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Credits"; -"about.items.join_community.caption" = "Community beitreten"; -"about.items.write_review.caption" = "Rezension schreiben"; -"about.items.share_twitter.caption" = "Darüber Twittern!"; -"about.items.website.caption" = "Homepage"; -"about.items.disclaimer.caption" = "Haftungsausschluss"; -"about.items.privacy_policy.caption" = "Datenschutzrichtlinie"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Version"; -"version.labels.intro" = "Passepartout und TunnelKit sind geschrieben und gewartet von Davide De Rosa (keeshux).\n\nQuellcode für Passepartout und TunnelKit ist öffentlich auf GitHub unter GPLv3 verfügbar, du findest die Links auf der Homepage."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Credits"; -"credits.sections.licenses.header" = "Lizenzen"; -"credits.sections.notices.header" = "Notizen"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Einstellungen"; -"preferences.sections.general.header" = "Allgemein"; -"preferences.items.launches_on_login.caption" = "Bei Anmeldung starten"; -"preferences.items.launches_on_login.footer" = "Hier ein Häkchen setzen, um die App beim Systemstart oder der Anmeldung automatisch zu starten."; -"preferences.items.confirm_quit.caption" = "Beenden bestätigen"; -"preferences.items.confirm_quit.footer" = "Hier ein Häkchen setzen, um einen Hinweis zur Bestätigung des Beendens anzuzeigen."; diff --git a/Passepartout/App/el.lproj/InfoPlist.strings b/Passepartout/App/el.lproj/InfoPlist.strings deleted file mode 100644 index 311cb789..00000000 --- a/Passepartout/App/el.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Πρόσβαση στο όνομα του τρέχοντος Wi-Fi"; - -"NSFaceIDUsageDescription" = "Ξεκλείδωμα εφαρμογής με Face ID"; diff --git a/Passepartout/App/el.lproj/Localizable.strings b/Passepartout/App/el.lproj/Localizable.strings deleted file mode 100644 index df665e25..00000000 --- a/Passepartout/App/el.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Πολιτική"; -"global.strings.networks" = "Δίκτυα"; -"global.strings.edit" = "Επεξεργασία"; -"global.strings.unknown" = "Άγνωστο"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Επιλέξτε ένα καταληκτικό σημείο"; - -"on_demand.sections.policy.footer.any" = "σε οποιοδήποτε δίκτυο"; -"on_demand.sections.policy.footer.including" = "μόνο στα παρακάτω δίκτυα"; -"on_demand.sections.policy.footer.excluding" = "εκτός από τα παρακάτω δίκτυα"; -"on_demand.policy.any" = "Όλα τα δίκτυα"; -"on_demand.policy.including" = "Συμπερίληψη"; -"on_demand.policy.excluding" = "Αποκλεισμός"; - -"settings.sections.icloud.footer" = "Απενεργοποιήστε τον συγχρονισμό για να επιτραπεί η διαγραφή. Για να διαγράψετε το κατάστημα iCloud με ασφάλεια, κάντε το σε όλες τις συγχρονισμένες συσκευές σας. Αυτό δεν θα επηρεάσει τα τοπικά προφίλ."; -"settings.items.should_enable_cloud_syncing.caption" = "Συγχρονισμός με iCloud"; -"settings.items.erase_cloud_store.caption" = "Διαγραφή καταστήματος iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Ακύρωση"; -"global.strings.next" = "Επόμενο"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Αποθήκευση"; -"global.strings.rename" = "Επανονομασία"; -"global.strings.duplicate" = "Δημιουργήστε αντίγραφο"; -"global.strings.add" = "Προσθήκη"; -"global.strings.delete" = "Διαγράψτε"; -"global.strings.uninstall" = "Καταργήστε την εγκατάσταση"; -"global.strings.default" = "Default"; -"global.strings.name" = "Όνομα"; -"global.strings.profiles" = "Προφίλ"; -"global.strings.provider" = "Πάροχος"; -"global.strings.providers" = "Πάροχοι"; -"global.strings.configuration" = "Ρύθμιση"; -"global.strings.address" = "Διεύθυνση"; -"global.strings.addresses" = "Διεθύνσεις"; -"global.strings.port" = "Θύρα"; -"global.strings.protocol" = "Πρωτόκολλο"; -"global.strings.protocols" = "Πρωτόκολλα"; -"global.strings.enabled" = "Ενεργοποιήθηκε"; -"global.strings.disabled" = "Απενεργοποιήθηκε"; -"global.strings.none" = "Κανένα"; -"global.strings.automatic" = "Αυτόματο"; -"global.strings.manual" = "Χειροκίνητο"; -"global.strings.encryption" = "Κρυπτογράφηση"; -"global.strings.reconnect" = "Επανασύνδεση"; -"global.strings.servers" = "Διακομιστές"; -"global.strings.domain" = "Domain"; -"global.strings.domains" = "Τομείς"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Διεπαφή"; -"global.strings.private_key" = "Ιδιωτικό κλειδί"; -"global.strings.public_key" = "Δημόσιο κλειδί"; -"global.strings.endpoint" = "Τελικό σημείο"; -"global.strings.keepalive" = "Διατηρήστε ζωντανή"; -"global.strings.advanced" = "Προηγμένα"; -"global.strings.translations" = "Μεταφράσεις"; -"global.strings.show" = "Προβολή"; -"global.strings.connect" = "Σύνδεση"; -"global.strings.disconnect" = "Αποσύνδεση"; -"global.strings.download" = "Λήψη"; -"global.strings.authentication" = "Αυθεντικοποίηση"; -"global.messages.unlock_app" = "Το πασπαρτού είναι κλειδωμένο"; -"global.messages.email_not_configured" = "Δεν έχει ρυθμιστεί λογαριασμός ηλεκτρονικού ταχυδρομείου."; -"global.messages.share" = "Το Passepartout είναι φιλικό προς το χρήστη, ανοιχτού κώδικα OpenVPN / WireGuard πρόγραμμα για iOS και macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Υπενθύμιση Αργότερα"; -"global.alerts.buttons.never" = "Μη με ρωτήσεις ξανά"; - -"global.placeholders.profile_name" = "Το προφίλ μου"; - -"global.errors.missing_profile" = "Λείπει προφίλ"; -"global.errors.missing_account" = "Λείπει λογαριασμός"; -"global.errors.missing_provider_server" = "Λείπει η τοποθεσία"; -"global.errors.missing_provider_preset" = "Λείπει προεπιλογή"; -"global.errors.tunnel_expired" = "H σύνδεση έληξε"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Υποστήριξη"; -"menu.all.share.title" = "Διαμοιράστε"; -"menu.all.about.title" = "Σχετικά με %@"; - -"menu.contextual.add_profile.from_files" = "Από τα αρχεία"; -"menu.contextual.add_profile.from_text" = "Από το κείμενο"; -"menu.contextual.add_profile.imported" = "Προσθήκη %@"; -"menu.contextual.support.write_review" = "Κριτική"; -"menu.contextual.support.join_community" = "Κοινότητα"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Πρόσκληση"; - -"menu.system.quit.title" = "Διακοπή %@"; - -"menu.system.quit.messages.confirm" = "Το VPN, αν είναι ενεργοποιημένο, θα εξακολουθεί να εκτελείται στο παρασκήνιο. Θέλετε να το διακόψετε?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Προσπάθεια Σύνδεσης"; -"tunnelkit.vpn.active" = "Ενεργό"; -"tunnelkit.vpn.disconnecting" = "Αποσυνδέετε"; -"tunnelkit.vpn.inactive" = "Μη ενεργό"; -"tunnelkit.vpn.disabled" = "Απενεργοποιημένο"; -"tunnelkit.vpn.unused" = "Ανενεργό"; - -"tunnelkit.errors.vpn.timeout" = "Τέλος χρονικού Ορίου"; -"tunnelkit.errors.vpn.dns" = "Το DNS απέτυχε"; -"tunnelkit.errors.vpn.auth" = "Το Auth απέτυχε"; -"tunnelkit.errors.vpn.tls" = "Το TLS απέτυχε"; -"tunnelkit.errors.vpn.encryption" = "Η Κρυπτογράφηση απέτυχε"; -"tunnelkit.errors.vpn.compression" = "Η συμπίεση δεν υποστηρίζεται"; -"tunnelkit.errors.vpn.network" = "Το δίκτυο άλλαξε"; -"tunnelkit.errors.vpn.routing" = "Λείπει η δρομολόγηση"; -"tunnelkit.errors.vpn.gateway" = "Δεν υπάρχει πύλη"; -"tunnelkit.errors.vpn.shutdown" = "Ο διακομιστής έκλεισε"; - -"tunnelkit.errors.parsing" = "Δεν είναι δυνατή η ανάλυση του παρεχόμενου αρχείου ρύθμισης παραμέτρων (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Το αρχείο ρυθμίσεων περιέχει μια ακατάλληλη επιλογή (%@)."; -"tunnelkit.errors.openvpn.required_option" = "Το αρχείο διαμόρφωσης δεν διαθέτει την απαιτούμενη επιλογή (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Το αρχείο διαμόρφωσης περιέχει μια επιλογή που δεν υποστηρίζεται (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Το αρχείο ρυθμίσεων είναι σωστό, αλλά περιέχει μια δυνητικά μη υποστηριζόμενη επιλογή (%@).\n\nΗ δυνατότητα σύνδεσης μπορεί να διακοπεί ανάλογα με τις ρυθμίσεις του διακομιστή."; -"tunnelkit.errors.openvpn.passphrase_required" = "Εισαγάγετε το κωδικό κρυπτογράφησης."; -"tunnelkit.errors.openvpn.decryption" = "Η διαμόρφωση περιέχει κρυπτογραφημένο ιδιωτικό κλειδί και δεν ήταν δυνατό να αποκρυπτογραφηθεί. Δείτε πάλι το κωδικό που καταχωρίσατε."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "Σε χρήση"; -"organizer.sections.tv.profiles_list.header.p1" = "Ανοίξτε το Passepartout στη συσκευή σας iOS ή macOS και ενεργοποιήστε την εναλλαγή \"Apple TV\" ενός προφίλ για να εμφανιστεί εδώ."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Δεν υπάρχουν προφίλ"; - -"organizer.alerts.reddit.message" = "Γνωρίζατε ότι το Passepartout έχει subreddit? Εγγραφείτε για ενημερώσεις ή για να συζητήσετε προβλήματα της εφαρμογές, νέες δυνατότητες και άλλα.\n\nΕίναι επίσης ένας ωραίος τρόπος να δείξετε ότι ενδιαφέρεστε για τη προσπάθεια αυτή."; -"organizer.alerts.reddit.buttons.subscribe" = "Εγγραφή τώρα!"; - -"organizer.alerts.remove_profile.message" = "Είστε βέβαιοι ότι θέλετε να διαγράψετε το προφίλ %@;"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Νέο προφίλ"; -"add_profile.shared.views.existing.header" = "Υπάρχον Προφίλ"; -"add_profile.shared.alerts.overwrite.message" = "Ένα προφίλ με το ίδιο όνομα υπάρχει ήδη. Αντικατάσταση;"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Εισαγωγή φράσης εισόδου"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Εδώ θα βρείτε ορισμένους παρόχους με προκαθορισμένες ρυθμίσεις προφίλ."; -"add_profile.provider.items.update_list" = "Αναβάθμιση Λίστας"; -"add_profile.provider.errors.no_default_server" = "Δεν βρέθηκε διακομιστής"; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Καλώς Ήλθατε στο Passepartout!\n\nΧρησιμοποιήστε τον διοργανωτή για να προσθέσετε ένα νέο προφίλ."; - -"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" = "Ανατροφοδότηση"; -"profile.items.use_profile.caption" = "Χρησιμοποιήστε αυτό το προφίλ"; -"profile.items.vpn_service.caption" = "Ενεργοποιήθηκε"; -"profile.items.vpn.turn_on.caption" = "Ενεργοποίηση VPN"; -"profile.items.vpn.turn_off.caption" = "Απενεργοποίηση VPN"; -"profile.items.connection_status.caption" = "Κατάσταση"; -"profile.items.data_count.caption" = "Ανταλλαγή δεδομένων"; -"profile.items.randomizes_server.caption" = "Τυχαίος διακομιστής"; -"profile.items.provider.refresh.caption" = "Ανανέωση της υποδομής"; -"profile.items.category.caption" = "Κατηγορία"; -"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;"; -"profile.alerts.uninstall_vpn.message" = "Θέλετε πραγματικά να διαγράψετε τη διαμόρφωση VPN από τις ρυθμίσεις της συσκευής σας; Αυτό μπορεί να διορθώσει κάποιες καταστραμμένες καταστάσεις VPN και δεν θα επηρεάσει τα προφίλ του παροχέα και του διακομιστή σας."; -"profile.alerts.test_connectivity.title" = "Συνδεσιμότητα"; -"profile.alerts.test_connectivity.messages.success" = "Η συσκευή σας είναι συνδεδεμένη στο Διαδίκτυο!"; -"profile.alerts.test_connectivity.messages.failure" = "Η συσκευή σας δεν διαθέτει σύνδεση στο Internet, παρακαλούμε να ελέγξετε τις παραμέτρους του προφίλ σας."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Τοποθεσία"; -"provider.location.sections.empty_favorites.footer" = "Σείρετε αριστερά για να προσθέσετε ή να αφαιρέσεται από τα αγαπημένα."; -"provider.location.actions.favorite" = "Αγαπημένο"; -"provider.location.actions.unfavorite" = "Δεν προτιμάται"; - -"provider.preset.title" = "Προεπιλογή"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Λογαριασμός"; -"account.sections.credentials.header" = "Διαπιστευτήρια"; -"account.sections.registration.footer" = "Πηγαίνετε να αποκτήσετε λογαριασμό στον ιστότοπο %@."; -"account.items.authentication_method.persistent" = "Επίμονο"; -"account.items.authentication_method.interactive" = "Διαδραστικό"; -"account.items.username.caption" = "Όνομα χρήστη"; -"account.items.username.placeholder" = "χρήστης"; -"account.items.password.caption" = "Κωδικός"; -"account.items.password.placeholder" = "κωδικός"; -"account.items.seed.caption" = "Τυχαίο κλειδί"; -"account.items.open_guide.caption" = "Δείτε τα διαπιστευτήρια σας"; -"account.items.signup.caption" = "Εγγραφείτε με %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Χρησιμοποιήστε τα διαπιστευτήρια της υπηρεσίας %@, τα οποία ενδέχεται να διαφέρουν από τα διαπιστευτήρια του ιστότοπου."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως αριθμητικό (χωρίς διαστήματα)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως το ηλεκτρονικό σας ταχυδρομείο."; -"account.sections.guidance.footer.infrastructure.pia" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως αριθμητικό με πρόθεμα \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Βρείτε τα διαπιστευτήριά σας %@ στην ενότητα \"Λογαριασμός> OpenVPN / IKEv2 Username \" της ιστοσελίδας."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως το ηλεκτρονικό σας ταχυδρομείο."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Χρησιμοποιήστε τα διαπιστευτήρια ιστοτόπου %@. Το όνομα χρήστη είναι συνήθως το ηλεκτρονικό σας ταχυδρομείο."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Βρείτε τα διαπιστευτήριά σας %@ στο OpenVPN Config Generator στον ιστότοπο."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Ομότιμο"; -"endpoint.wireguard.items.preshared_key.caption" = "Κλειδί προδιαμοιρασμού"; -"endpoint.wireguard.items.allowed_ip.caption" = "Επιτρεπόμενη IP"; - -"endpoint.advanced.title" = "Τεχνικές Λεπτομέρειες"; -"endpoint.advanced.openvpn.sections.pull.header" = "Τραβήξτε από τον διακομιστή"; -"endpoint.advanced.openvpn.sections.communication.header" = "Επικοινωνία"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Αν καταλήξατε σε κατεστραμένη συνδεσιμότητα μετά την αλλαγή των παραμέτρων επικοινωνίας, πατήστε για να επανέλθετε στην αρχική διαμόρφωση."; -"endpoint.advanced.openvpn.sections.compression.header" = "Συμπίεση"; -"endpoint.advanced.openvpn.sections.network.header" = "Δίκτυο"; -"endpoint.advanced.openvpn.sections.other.header" = "Άλλο"; -"endpoint.advanced.openvpn.items.route.caption" = "Διαδρομή"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cipher"; -"endpoint.advanced.openvpn.items.digest.caption" = "Αυθεντικοποίηση"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Ενσωματωμένο"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Αλγόρυθμος"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Δεν υποστηρίζεται"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Επαναφορά ρυθμίσεων"; -"endpoint.advanced.openvpn.items.client.caption" = "Πιστοποιητικό"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Κλειδί"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Επαληθεύτηκε"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Δεν επαληθεύτηκε"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Αυθεντικοποίηση"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Κρυπτογράφηση"; -"endpoint.advanced.openvpn.items.eku.caption" = "Εκτεταμένη επαλήθευση"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d δευτερόλεπτα"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Επαναδιαπραγμάτευση"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "μετά από %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Τυχαίο τελικό σημείο"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Τυχαιοποίηση ονομάτων κεντρικών υπολογιστών"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Ρυθμίσεις Δικτύου"; -"network_settings.sections.choices.header" = "Παράκαμψη"; -"network_settings.gateway.title" = "Προεπιλεγμένη πύλη"; -"network_settings.proxy.items.bypass_domains.caption" = "Παράκαμψη τομέων"; -"network_settings.items.add_dns_server.caption" = "Προσθήκη Διεύθυνσης"; -"network_settings.items.add_dns_domain.caption" = "Προσθήκη τομέα αναζήτησης"; -"network_settings.items.proxy_bypass.caption" = "Παράκαμψη Τομέα"; -"network_settings.items.add_proxy_bypass.caption" = "Προσθήκη τομέα παράκαμψης"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "Κατά παραγγελία"; -"on_demand.sections.policy.footer" = "Ενεργοποιήστε το VPN %@."; -"on_demand.items.add_ssid.caption" = "Προσθέστε Wi-Fi"; -"on_demand.items.active.caption" = "Εμπιστευθείτε"; -"on_demand.items.mobile.caption" = "Δίκτυο Κινητής"; -"on_demand.items.ethernet.caption" = "Ενσύρματες συνδέσεις"; - -"on_demand.items.policy.caption" = "Τα αξιόπιστα δίκτυα απενεργοποιούν το VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Διαγνωστικά"; -"diagnostics.sections.debug_log.footer" = "Η κατάσταση κάλυψης θα είναι αποτελεσματική μετά την επανασύνδεση. Τα δεδομένα δικτύου είναι του διακομιστή, διευθύνσεις IP, δρομολόγηση και SSID. Τα διαπιστευτήρια και τα ιδιωτικά κλειδιά δεν καταγράφονται ανεξάρτητα."; -"diagnostics.items.server_configuration.caption" = "Ρυθμίσεις Διακομιστή"; -"diagnostics.items.app_log.title" = "Εφαρμογή"; -"diagnostics.items.masks_private_data.caption" = "Μάσκα δεδομένα δικτύου"; -"diagnostics.items.report_issue.caption" = "Αναφορά ζητήματος συνδεσιμότητας"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Για να επαναφέρετε με ασφάλεια την τρέχουσα καταγραφή εντοπισμού σφαλμάτων και να εφαρμόσετε τη νέα προτίμηση κάλυψης, πρέπει να συνδεθείτε ξανά με το VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Μητρώο εντοπισμού σφαλμάτων"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Αντιγραφή"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Αναφορά Προβλήματος"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Προσθήκη Συντόμευσης"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Δίκτυο Κινητής"; -"shortcuts.add.items.connect.caption" = "Σύνδεση σε"; -"shortcuts.add.items.enable_vpn.caption" = "Ενεργοποίηση VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Απενεργοποίηση VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Εμπιστέψου το τρέχον Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Μην εμπιστευθείτε το τρέχον Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Εμπιστοσύνη δικτύου κινητής τηλεφωνίας"; -"shortcuts.add.items.untrust_cellular.caption" = "Μην εμπιστευθείτε το δίκτυο κινητής τηλεφωνίας"; -"shortcuts.add.alerts.no_profiles.message" = "Δεν υπάρχει προφίλ για σύνδεση."; - -"shortcuts.edit.title" = "Διαχείριση συντομεύσεων"; -"shortcuts.edit.sections.all.header" = "Υπάρχουσες συντομεύσεις"; -"shortcuts.edit.sections.add.footer" = "Get help from Siri to speed up your most common interactions with the app."; -"shortcuts.edit.items.add_shortcut.caption" = "Προσθήκη Συντόμευσης"; - -/* MARK: PaywallView */ - -"paywall.title" = "Αγορά"; -"paywall.sections.products.footer" = "Κάθε προϊόν είναι μια αγορά. Οι αγορές παρόχων δεν περιλαμβάνουν τη συνδρομή VPN."; -"paywall.items.loading.caption" = "Φόρτωση προϊόντων"; -"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 */ - -"donate.title" = "Δωρεά"; -"donate.sections.one_time.header" = "Μια Φορά"; -"donate.sections.one_time.footer" = "Αν είστε χαρούμενη με τη δουλειά μου, εδώ είναι λίγα ποσά που μπορείτε να δώσετε αμέσως.\n\nΘα χρεωθείτε μόνο μία φορά και μπορείτε να δώσετε πολλές φορές."; -"donate.items.loading.caption" = "Φόρτωση δωρεών"; -"donate.items.purchasing.caption" = "Εκτέλεση δωρεάς"; -"donate.alerts.purchase.success.message" = "Αυτό σημαίνει πολλά για μένα και πραγματικά ελπίζω να συνεχίσετε να χρησιμοποιείτε και να προωθείτε αυτήν την εφαρμογή."; -"donate.alerts.purchase.failure.message" = "Δεν είναι δυνατή η εκτέλεση της δωρεάς. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Ρυθμίσεις"; -"settings.items.locks_in_background.caption" = "Κλείδωμα πρόσβασης στην εφαρμογή"; -"settings.items.donate.caption" = "Κάντε μια δωρεά"; - -/* MARK: AboutView */ - -"about.title" = "Περι"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Συντελεστές"; -"about.items.join_community.caption" = "Συμμετοχή στην κοινότητα"; -"about.items.write_review.caption" = "Γράψτε μια κριτική"; -"about.items.share_twitter.caption" = "Tweet γι 'αυτό!"; -"about.items.website.caption" = "Αρχική Σελίδα"; -"about.items.disclaimer.caption" = "Άρνηση Ευθύνης"; -"about.items.privacy_policy.caption" = "Πολιτική Απορρήτου"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Έκδοση"; -"version.labels.intro" = "Το Passepartout και το TunnelKit γράφονται και συντηρούνται από τον Davide De Rosa (keeshux).\n\nΟ πηγαίος κώδικας για το Passepartout και το TunnelKit είναι δημόσια διαθέσιμε στο GitHub υπό το GPLv3, μπορείτε να βρείτε συνδέσμους στην αρχική σελίδα."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Συντελεστές"; -"credits.sections.licenses.header" = "Άδειες"; -"credits.sections.notices.header" = "Σημειώσεις"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Προτιμήσεις"; -"preferences.sections.general.header" = "Γενικός"; -"preferences.items.launches_on_login.caption" = "Εκκίνηση κατά τη σύνδεση"; -"preferences.items.launches_on_login.footer" = "Επιλέξτε για αυτόματη εκκίνηση της εφαρμογής κατά την εκκίνηση ή τη σύνδεση."; -"preferences.items.confirm_quit.caption" = "Επιβεβαίωση διακοπής"; -"preferences.items.confirm_quit.footer" = "Επιλέξτε για παρουσίαση ειδοποίησης ότι επιβεβαιώνεται η διακοπή."; diff --git a/Passepartout/App/en.lproj/InfoPlist.strings b/Passepartout/App/en.lproj/InfoPlist.strings deleted file mode 100644 index 9dc3741a..00000000 --- a/Passepartout/App/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Access name of current Wi-Fi"; - -"NSFaceIDUsageDescription" = "Unlock app with Face ID"; diff --git a/Passepartout/App/en.lproj/Localizable.strings b/Passepartout/App/en.lproj/Localizable.strings deleted file mode 100644 index ea0081eb..00000000 --- a/Passepartout/App/en.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Policy"; -"global.strings.networks" = "Networks"; -"global.strings.edit" = "Edit"; -"global.strings.unknown" = "Unknown"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Please select an endpoint"; - -"on_demand.sections.policy.footer.any" = "in any network"; -"on_demand.sections.policy.footer.including" = "only in the networks below"; -"on_demand.sections.policy.footer.excluding" = "except in the networks below"; -"on_demand.policy.any" = "All networks"; -"on_demand.policy.including" = "Include"; -"on_demand.policy.excluding" = "Exclude"; - -"settings.sections.icloud.footer" = "Disable sync to allow erase. To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles."; -"settings.items.should_enable_cloud_syncing.caption" = "Sync with iCloud"; -"settings.items.erase_cloud_store.caption" = "Erase iCloud store"; - -/* MARK: Global */ - -"global.strings.cancel" = "Cancel"; -"global.strings.next" = "Next"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Save"; -"global.strings.rename" = "Rename"; -"global.strings.duplicate" = "Duplicate"; -"global.strings.add" = "Add"; -"global.strings.delete" = "Delete"; -"global.strings.uninstall" = "Uninstall"; -"global.strings.default" = "Default"; -"global.strings.name" = "Name"; -"global.strings.profiles" = "Profiles"; -"global.strings.provider" = "Provider"; -"global.strings.providers" = "Providers"; -"global.strings.configuration" = "Configuration"; -"global.strings.address" = "Address"; -"global.strings.addresses" = "Addresses"; -"global.strings.port" = "Port"; -"global.strings.protocol" = "Protocol"; -"global.strings.protocols" = "Protocols"; -"global.strings.enabled" = "Enabled"; -"global.strings.disabled" = "Disabled"; -"global.strings.none" = "None"; -"global.strings.automatic" = "Automatic"; -"global.strings.manual" = "Manual"; -"global.strings.encryption" = "Encryption"; -"global.strings.reconnect" = "Reconnect"; -"global.strings.servers" = "Servers"; -"global.strings.domain" = "Domain"; -"global.strings.domains" = "Domains"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interface"; -"global.strings.private_key" = "Private key"; -"global.strings.public_key" = "Public key"; -"global.strings.endpoint" = "Endpoint"; -"global.strings.keepalive" = "Keep-alive"; -"global.strings.advanced" = "Advanced"; -"global.strings.translations" = "Translations"; -"global.strings.show" = "Show"; -"global.strings.connect" = "Connect"; -"global.strings.disconnect" = "Disconnect"; -"global.strings.download" = "Download"; -"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"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Remind me later"; -"global.alerts.buttons.never" = "Don't ask again"; - -"global.placeholders.profile_name" = "My profile"; - -"global.errors.missing_profile" = "Missing profile"; -"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 */ - -"menu.all.support.title" = "Support"; -"menu.all.share.title" = "Share"; -"menu.all.about.title" = "About %@"; - -"menu.contextual.add_profile.from_files" = "From Files"; -"menu.contextual.add_profile.from_text" = "From text"; -"menu.contextual.add_profile.imported" = "Add %@"; -"menu.contextual.support.write_review" = "Review"; -"menu.contextual.support.join_community" = "Community"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Invite"; - -"menu.system.quit.title" = "Quit %@"; - -"menu.system.quit.messages.confirm" = "The VPN, if enabled, will still run in the background. Do you want to quit?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Connecting"; -"tunnelkit.vpn.active" = "Active"; -"tunnelkit.vpn.disconnecting" = "Disconnecting"; -"tunnelkit.vpn.inactive" = "Inactive"; -"tunnelkit.vpn.disabled" = "Disabled"; -"tunnelkit.vpn.unused" = "Off"; - -"tunnelkit.errors.vpn.timeout" = "Timeout"; -"tunnelkit.errors.vpn.dns" = "DNS failed"; -"tunnelkit.errors.vpn.auth" = "Auth failed"; -"tunnelkit.errors.vpn.tls" = "TLS failed"; -"tunnelkit.errors.vpn.encryption" = "Encryption failed"; -"tunnelkit.errors.vpn.compression" = "Compression unsupported"; -"tunnelkit.errors.vpn.network" = "Network changed"; -"tunnelkit.errors.vpn.routing" = "Missing routing"; -"tunnelkit.errors.vpn.gateway" = "No gateway"; -"tunnelkit.errors.vpn.shutdown" = "Server shutdown"; - -"tunnelkit.errors.parsing" = "Unable to parse the provided configuration file (%@)."; -"tunnelkit.errors.openvpn.malformed" = "The configuration file contains a malformed option (%@)."; -"tunnelkit.errors.openvpn.required_option" = "The configuration file lacks a required option (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "The configuration file contains an unsupported option (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "The configuration file is correct but contains a potentially unsupported option (%@).\n\nConnectivity may break depending on server settings."; -"tunnelkit.errors.openvpn.passphrase_required" = "Please enter the encryption passphrase."; -"tunnelkit.errors.openvpn.decryption" = "Unable to decrypt private key."; - -/* 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"; - -"organizer.alerts.reddit.message" = "Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project."; -"organizer.alerts.reddit.buttons.subscribe" = "Subscribe now!"; - -"organizer.alerts.remove_profile.message" = "Are you sure you want to delete profile %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "New profile"; -"add_profile.shared.views.existing.header" = "Existing profiles"; -"add_profile.shared.alerts.overwrite.message" = "A profile with the same name already exists. Replace it?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Enter passphrase"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Here you find a few providers with preset configuration profiles."; -"add_profile.provider.items.update_list" = "Update list"; -"add_profile.provider.errors.no_default_server" = "Could not find any server."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Welcome to Passepartout!\n\nUse the organizer to add a new profile."; - -"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"; -"profile.items.use_profile.caption" = "Use this profile"; -"profile.items.vpn_service.caption" = "Enabled"; -"profile.items.vpn.turn_on.caption" = "Enable VPN"; -"profile.items.vpn.turn_off.caption" = "Disable VPN"; -"profile.items.connection_status.caption" = "Status"; -"profile.items.data_count.caption" = "Exchanged data"; -"profile.items.randomizes_server.caption" = "Randomize server"; -"profile.items.provider.refresh.caption" = "Refresh infrastructure"; -"profile.items.category.caption" = "Category"; -"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?"; -"profile.alerts.uninstall_vpn.message" = "Do you really want to erase the VPN configuration from your device settings? This may fix some broken VPN states and will not affect your provider and host profiles."; -"profile.alerts.test_connectivity.title" = "Connectivity"; -"profile.alerts.test_connectivity.messages.success" = "Your device is connected to the Internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Your device has no Internet connectivity, please review your profile parameters."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Location"; -"provider.location.sections.empty_favorites.footer" = "Swipe left on a location to add or remove it from Favorites."; -"provider.location.actions.favorite" = "Favorite"; -"provider.location.actions.unfavorite" = "Unfavorite"; - -"provider.preset.title" = "Preset"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Account"; -"account.sections.credentials.header" = "Credentials"; -"account.sections.registration.footer" = "Go get an account on the %@ website."; -"account.items.authentication_method.persistent" = "Persistent"; -"account.items.authentication_method.interactive" = "Interactive"; -"account.items.username.caption" = "Username"; -"account.items.username.placeholder" = "username"; -"account.items.password.caption" = "Password"; -"account.items.password.placeholder" = "secret"; -"account.items.seed.caption" = "Seed"; -"account.items.open_guide.caption" = "See your credentials"; -"account.items.signup.caption" = "Register with %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Use your %@ website credentials."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Use your %@ service credentials, which may differ from website credentials."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Use your %@ website credentials. Your username is usually numeric (without spaces)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Use your %@ website credentials. Your username is usually your e-mail."; -"account.sections.guidance.footer.infrastructure.pia" = "Use your %@ website credentials. Your username is usually numeric with a \"p\" prefix."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Find your %@ credentials in the \"Account > OpenVPN / IKEv2 Username\" section of the website."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Use your %@ website credentials. Your username is usually your e-mail."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Use your %@ website credentials. Your username is usually your e-mail."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Find your %@ credentials in the OpenVPN Config Generator on the website."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Preshared key"; -"endpoint.wireguard.items.allowed_ip.caption" = "Allowed IP"; - -"endpoint.advanced.title" = "Technical details"; -"endpoint.advanced.openvpn.sections.pull.header" = "Pull from server"; -"endpoint.advanced.openvpn.sections.communication.header" = "Communication"; -"endpoint.advanced.openvpn.sections.reset.footer" = "If you ended up with broken connectivity after changing the communication parameters, tap to revert to the original configuration."; -"endpoint.advanced.openvpn.sections.compression.header" = "Compression"; -"endpoint.advanced.openvpn.sections.network.header" = "Network"; -"endpoint.advanced.openvpn.sections.other.header" = "Other"; -"endpoint.advanced.openvpn.items.route.caption" = "Route"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cipher"; -"endpoint.advanced.openvpn.items.digest.caption" = "Authentication"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Embedded"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algorithm"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Unsupported"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Reset configuration"; -"endpoint.advanced.openvpn.items.client.caption" = "Certificate"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Key"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Verified"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Not verified"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Authentication"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Encryption"; -"endpoint.advanced.openvpn.items.eku.caption" = "Extended verification"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d seconds"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Renegotiation"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "after %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Randomize endpoint"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Randomize hostnames"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Network settings"; -"network_settings.sections.choices.header" = "Override"; -"network_settings.gateway.title" = "Default gateway"; -"network_settings.proxy.items.bypass_domains.caption" = "Bypass domains"; -"network_settings.items.add_dns_server.caption" = "Add address"; -"network_settings.items.add_dns_domain.caption" = "Add search domain"; -"network_settings.items.proxy_bypass.caption" = "Bypass domain"; -"network_settings.items.add_proxy_bypass.caption" = "Add bypass domain"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "On demand"; -"on_demand.sections.policy.footer" = "Activate the VPN %@."; -"on_demand.items.add_ssid.caption" = "Add Wi-Fi"; -"on_demand.items.active.caption" = "Trust"; -"on_demand.items.mobile.caption" = "Cellular network"; -"on_demand.items.ethernet.caption" = "Wired connections"; - -"on_demand.items.policy.caption" = "Trust disables VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnostics"; -"diagnostics.sections.debug_log.footer" = "Masking status will be effective after reconnecting. Network data are hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless."; -"diagnostics.items.server_configuration.caption" = "Server configuration"; -"diagnostics.items.app_log.title" = "App"; -"diagnostics.items.masks_private_data.caption" = "Mask network data"; -"diagnostics.items.report_issue.caption" = "Report connectivity issue"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Debug log"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Copy"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Report issue"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Add shortcut"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Cellular"; -"shortcuts.add.items.connect.caption" = "Connect to"; -"shortcuts.add.items.enable_vpn.caption" = "Enable VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Disable VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Trust current Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Untrust current Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Trust cellular network"; -"shortcuts.add.items.untrust_cellular.caption" = "Untrust cellular network"; -"shortcuts.add.alerts.no_profiles.message" = "There is no profile to connect to."; - -"shortcuts.edit.title" = "Manage shortcuts"; -"shortcuts.edit.sections.all.header" = "Existing shortcuts"; -"shortcuts.edit.sections.add.footer" = "Get help from Siri to speed up your most common interactions with the app."; -"shortcuts.edit.items.add_shortcut.caption" = "Add shortcut"; - -/* MARK: PaywallView */ - -"paywall.title" = "Purchase"; -"paywall.sections.products.footer" = "Every product is a one-time purchase. Provider purchases do not include a VPN subscription."; -"paywall.items.loading.caption" = "Loading products"; -"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 */ - -"donate.title" = "Donate"; -"donate.sections.one_time.header" = "One time"; -"donate.sections.one_time.footer" = "If you want to display gratitude for my free work, here are a couple amounts you can donate instantly.\n\nYou will only be charged once per donation, and you can donate multiple times."; -"donate.items.loading.caption" = "Loading donations"; -"donate.items.purchasing.caption" = "Performing donation"; -"donate.alerts.purchase.success.message" = "This means a lot to me and I really hope you keep using and promoting this app."; -"donate.alerts.purchase.failure.message" = "Unable to perform the donation. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Settings"; -"settings.items.locks_in_background.caption" = "Lock app access"; -"settings.items.donate.caption" = "Make a donation"; - -/* MARK: AboutView */ - -"about.title" = "About"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Credits"; -"about.items.join_community.caption" = "Join community"; -"about.items.write_review.caption" = "Write a review"; -"about.items.share_twitter.caption" = "Tweet about it!"; -"about.items.website.caption" = "Home page"; -"about.items.disclaimer.caption" = "Disclaimer"; -"about.items.privacy_policy.caption" = "Privacy policy"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Version"; -"version.labels.intro" = "Passepartout and TunnelKit are written and maintained by Davide De Rosa (keeshux).\n\nSource code for Passepartout and TunnelKit is publicly available on GitHub under the GPLv3, you can find links in the home page."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Credits"; -"credits.sections.licenses.header" = "Licenses"; -"credits.sections.notices.header" = "Notices"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Preferences"; -"preferences.sections.general.header" = "General"; -"preferences.items.launches_on_login.caption" = "Launch on login"; -"preferences.items.launches_on_login.footer" = "Check to automatically launch the app on boot or login."; -"preferences.items.confirm_quit.caption" = "Confirm quit"; -"preferences.items.confirm_quit.footer" = "Check to present a quit confirmation alert."; diff --git a/Passepartout/App/es.lproj/InfoPlist.strings b/Passepartout/App/es.lproj/InfoPlist.strings deleted file mode 100644 index 8c979cc0..00000000 --- a/Passepartout/App/es.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Acceder al nombre de la red Wi-Fi corriente"; - -"NSFaceIDUsageDescription" = "Desbloquear aplicación con Face ID"; diff --git a/Passepartout/App/es.lproj/Localizable.strings b/Passepartout/App/es.lproj/Localizable.strings deleted file mode 100644 index acb0f582..00000000 --- a/Passepartout/App/es.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Política"; -"global.strings.networks" = "Redes"; -"global.strings.edit" = "Modificar"; -"global.strings.unknown" = "Desconocido"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Seleccionar un extremo"; - -"on_demand.sections.policy.footer.any" = "en cualquier red"; -"on_demand.sections.policy.footer.including" = "sólo en las redes abajo"; -"on_demand.sections.policy.footer.excluding" = "excepto en las redes abajo"; -"on_demand.policy.any" = "Todas las redes"; -"on_demand.policy.including" = "Incluir"; -"on_demand.policy.excluding" = "Excluir"; - -"settings.sections.icloud.footer" = "Deshabilitar la sincronización para esta operación. Para borrar el store iCloud, repetir en todos los dispositivos sincronizados. Esto no afectará los perfiles locales."; -"settings.items.should_enable_cloud_syncing.caption" = "Sincronizar con iCloud"; -"settings.items.erase_cloud_store.caption" = "Borrar store iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Cancelar"; -"global.strings.next" = "Siguiente"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Guardar"; -"global.strings.rename" = "Cambiar nombre"; -"global.strings.duplicate" = "Duplicar"; -"global.strings.add" = "Añadir"; -"global.strings.delete" = "Borrar"; -"global.strings.uninstall" = "Desinstalar"; -"global.strings.default" = "Default"; -"global.strings.name" = "Nombre"; -"global.strings.profiles" = "Perfiles"; -"global.strings.provider" = "Proveedor"; -"global.strings.providers" = "Proveedores"; -"global.strings.configuration" = "Configuración"; -"global.strings.address" = "Dirección"; -"global.strings.addresses" = "Direcciones"; -"global.strings.port" = "Puerta"; -"global.strings.protocol" = "Protocolo"; -"global.strings.protocols" = "Protocolos"; -"global.strings.enabled" = "Habilitado"; -"global.strings.disabled" = "Deshabilitado"; -"global.strings.none" = "Ninguno"; -"global.strings.automatic" = "Automático"; -"global.strings.manual" = "Manual"; -"global.strings.encryption" = "Cifrado"; -"global.strings.reconnect" = "Reconectar"; -"global.strings.servers" = "Servidores"; -"global.strings.domain" = "Dominio"; -"global.strings.domains" = "Dominios"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interfaz"; -"global.strings.private_key" = "Clave privada"; -"global.strings.public_key" = "Clave pública"; -"global.strings.endpoint" = "Extremo"; -"global.strings.keepalive" = "Keep-alive"; -"global.strings.advanced" = "Avanzado"; -"global.strings.translations" = "Traducciones"; -"global.strings.show" = "Mostrar"; -"global.strings.connect" = "Conectar"; -"global.strings.disconnect" = "Desconectar"; -"global.strings.download" = "Descargar"; -"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"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Recordar más tarde"; -"global.alerts.buttons.never" = "No preguntar más"; - -"global.placeholders.profile_name" = "Mi perfil"; - -"global.errors.missing_profile" = "Sin perfil"; -"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 */ - -"menu.all.support.title" = "Soporte"; -"menu.all.share.title" = "Compartir"; -"menu.all.about.title" = "Sobre %@"; - -"menu.contextual.add_profile.from_files" = "Desde Ficheros"; -"menu.contextual.add_profile.from_text" = "Desde texto"; -"menu.contextual.add_profile.imported" = "Añadir %@"; -"menu.contextual.support.write_review" = "Reseñar"; -"menu.contextual.support.join_community" = "Comunidad"; -"menu.contextual.share_twitter" = "Tweetear"; -"menu.contextual.share_generic" = "Invitar"; - -"menu.system.quit.title" = "Salir de %@"; - -"menu.system.quit.messages.confirm" = "Si la VPN está habilitada, seguirá funcionando en segundo plano. ¿Deseas salir?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Conectando"; -"tunnelkit.vpn.active" = "Activo"; -"tunnelkit.vpn.disconnecting" = "Desconectando"; -"tunnelkit.vpn.inactive" = "Inactivo"; -"tunnelkit.vpn.disabled" = "Deshabilitado"; -"tunnelkit.vpn.unused" = "Desactivada"; - -"tunnelkit.errors.vpn.timeout" = "Timeout"; -"tunnelkit.errors.vpn.dns" = "DNS fallido"; -"tunnelkit.errors.vpn.auth" = "Autentificación fallida"; -"tunnelkit.errors.vpn.tls" = "TLS fallido"; -"tunnelkit.errors.vpn.encryption" = "Cifrado fallido"; -"tunnelkit.errors.vpn.compression" = "Compresión no soportada"; -"tunnelkit.errors.vpn.network" = "Cambio de red"; -"tunnelkit.errors.vpn.routing" = "Sin rutas"; -"tunnelkit.errors.vpn.gateway" = "Sin puerta de enlace"; -"tunnelkit.errors.vpn.shutdown" = "Servidor apagado"; - -"tunnelkit.errors.parsing" = "Imposible importar el fichero de configuración proporcionado (%@)."; -"tunnelkit.errors.openvpn.malformed" = "El fichero de configuración contiene una opción mal formada (%@)."; -"tunnelkit.errors.openvpn.required_option" = "El fichero de configuración falta de una opción necesaria (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "El fichero de configuración contiene una opción no soportada (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "El fichero de configuración es correcto pero contiene una opción potencialmente no soportada (%@).\n\nLa conectividad podría fallar según los parámetros del servidor."; -"tunnelkit.errors.openvpn.passphrase_required" = "Por favor introducir la contraseña de cifrado."; -"tunnelkit.errors.openvpn.decryption" = "La configuración contiene una clave privada cifrada que no ha podido ser descifrada. Por favor revisa la contraseña introducida."; - -/* 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"; - -"organizer.alerts.reddit.message" = "Sabías que Passepartout tiene un subreddit? Suscríbete para actualizaciones o comentar problemas, funciones, nuevas plataformas o todo lo que se te ocurra.\n\nTambién es la manera ideal de mostrar interés en este proyecto."; -"organizer.alerts.reddit.buttons.subscribe" = "Suscribir ahora!"; - -"organizer.alerts.remove_profile.message" = "¿Seguro que deseas eliminar el perfil %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Nuevo perfil"; -"add_profile.shared.views.existing.header" = "Perfiles existentes"; -"add_profile.shared.alerts.overwrite.message" = "Ya existe un perfil con el mismo nombre. Reemplazar?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Introduce la contraseña de cifrado"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Aquí encuentras algunos proveedores con ajustes preconfigurados."; -"add_profile.provider.items.update_list" = "Actualizar lista"; -"add_profile.provider.errors.no_default_server" = "No se ha encontrado ningún servidor."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Bienvenid@ a Passepartout!\n\nUsa el organizador para añadir un nuevo perfil."; - -"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"; -"profile.items.use_profile.caption" = "Usar este perfil"; -"profile.items.vpn_service.caption" = "Habilitado"; -"profile.items.vpn.turn_on.caption" = "Habilitar VPN"; -"profile.items.vpn.turn_off.caption" = "Deshabilitar VPN"; -"profile.items.connection_status.caption" = "Estado"; -"profile.items.data_count.caption" = "Datos intercambiados"; -"profile.items.randomizes_server.caption" = "Servidor aleatorio"; -"profile.items.provider.refresh.caption" = "Refrescar infraestructura"; -"profile.items.category.caption" = "Categoría"; -"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?"; -"profile.alerts.uninstall_vpn.message" = "Realmente quieres eliminar la configuración VPN de tu dispositivo? Ésto puede corregir algunos estados incorrectos del VPN y no afectará tus perfiles."; -"profile.alerts.test_connectivity.title" = "Conectividad"; -"profile.alerts.test_connectivity.messages.success" = "Tu dispositivo está conectado en Internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Tu dispositivo no tiene conectividad Internet, por favor revisa los parámetros de tu perfil."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Ubicación"; -"provider.location.sections.empty_favorites.footer" = "Desliza a la izquierda de una ubicación para agregarla o quitarla de los Favoritos."; -"provider.location.actions.favorite" = "Favorita"; -"provider.location.actions.unfavorite" = "No favorita"; - -"provider.preset.title" = "Ajuste"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Cuenta"; -"account.sections.credentials.header" = "Credenciales"; -"account.sections.registration.footer" = "Obten una cuenta en la web de %@."; -"account.items.authentication_method.persistent" = "Persistente"; -"account.items.authentication_method.interactive" = "Interactivo"; -"account.items.username.caption" = "Usuario"; -"account.items.username.placeholder" = "usuario"; -"account.items.password.caption" = "Contraseña"; -"account.items.password.placeholder" = "secreto"; -"account.items.seed.caption" = "Semilla"; -"account.items.open_guide.caption" = "Mira tus credenciales"; -"account.items.signup.caption" = "Registrarse con %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Usa tus credenciales de la web %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Usa tus credenciales de servicio %@, que pueden diferir de las credenciales de la web."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Usa tus credenciales de la web %@. Normalmente tu usuario es numérico (sin espacios)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Usa tus credenciales de la web %@. Normalmente tu usuario es tu e-mail."; -"account.sections.guidance.footer.infrastructure.pia" = "Usa tus credenciales de la web %@. Normalmente tu usuario es numérico con un prefijo \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Encuentra tus credenciales %@ en la sección \"Account > OpenVPN / IKEv2 Username\" de la web."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Usa tus credenciales de la web %@. Normalmente tu usuario es tu e-mail."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Usa tus credenciales de la web %@. Normalmente tu usuario es tu e-mail."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Encuentra tus credenciales %@ en el \"OpenVPN Config Generator\" en la web."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Clave previamente compartida"; -"endpoint.wireguard.items.allowed_ip.caption" = "IP permitida"; - -"endpoint.advanced.title" = "Detalles técnicos"; -"endpoint.advanced.openvpn.sections.pull.header" = "Obtener del servidor"; -"endpoint.advanced.openvpn.sections.communication.header" = "Comunicación"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Si acabaste estropeando tu conectividad tras cambiar los parámetros de comunicación, pulsa para volver a la configuración inicial."; -"endpoint.advanced.openvpn.sections.compression.header" = "Compresión"; -"endpoint.advanced.openvpn.sections.network.header" = "Red"; -"endpoint.advanced.openvpn.sections.other.header" = "Otro"; -"endpoint.advanced.openvpn.items.route.caption" = "Ruta"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cifrado"; -"endpoint.advanced.openvpn.items.digest.caption" = "Autentificación"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Incluida"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Marco"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algoritmo"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "No soportado"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Resetear configuración"; -"endpoint.advanced.openvpn.items.client.caption" = "Certificado"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Clave"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Verificado"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "No verificado"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Envoltorio"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Autentificado"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Cifrado"; -"endpoint.advanced.openvpn.items.eku.caption" = "Verificación extendida"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d segundos"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Renegociación"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "después de %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Destino aleatorio"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Hostname aleatorio"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Ajustes de red"; -"network_settings.sections.choices.header" = "Customizar"; -"network_settings.gateway.title" = "Puerta de enlace"; -"network_settings.proxy.items.bypass_domains.caption" = "Dominios ignorados"; -"network_settings.items.add_dns_server.caption" = "Añadir dirección"; -"network_settings.items.add_dns_domain.caption" = "Añadir dominio"; -"network_settings.items.proxy_bypass.caption" = "Dominio ignorado"; -"network_settings.items.add_proxy_bypass.caption" = "Añadir dominio ignorado"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "On demand"; -"on_demand.sections.policy.footer" = "Activar la VPN %@."; -"on_demand.items.add_ssid.caption" = "Añadir Wi-Fi"; -"on_demand.items.active.caption" = "Confianza"; -"on_demand.items.mobile.caption" = "Red móvil"; -"on_demand.items.ethernet.caption" = "Conexiones cableadas"; - -"on_demand.items.policy.caption" = "Red de confianza deshabilita el VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnósticos"; -"diagnostics.sections.debug_log.footer" = "El estado de ocultación será efectivo tras reconectar. Los datos de red son hostnames, direcciones IP, routing, SSID. Las credenciales y las claves privadas no son registrados a pesar."; -"diagnostics.items.server_configuration.caption" = "Configuración del servidor"; -"diagnostics.items.app_log.title" = "App"; -"diagnostics.items.masks_private_data.caption" = "Ocultar datos de red"; -"diagnostics.items.report_issue.caption" = "Reportar problema de conectividad"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Para resetear el registro de debug y aplicar la nueva preferencia de ocultación, debes reconectarte al VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Registro de debug"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Copiar"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Reportar incidencia"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Añadir atajo"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Móvil"; -"shortcuts.add.items.connect.caption" = "Conectar a"; -"shortcuts.add.items.enable_vpn.caption" = "Habilitar VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Deshabilitar VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Añadir Wi-Fi de confianza"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Borrar Wi-Fi de confianza"; -"shortcuts.add.items.trust_cellular.caption" = "Añadir red móvil de confianza"; -"shortcuts.add.items.untrust_cellular.caption" = "Borrar red móvil de confianza"; -"shortcuts.add.alerts.no_profiles.message" = "No hay ningún perfil al que conectarse."; - -"shortcuts.edit.title" = "Gestionar atajos"; -"shortcuts.edit.sections.all.header" = "Atajos existentes"; -"shortcuts.edit.sections.add.footer" = "Déjate ayudar por Siri para acelerar tus interacciones más frecuentes con la aplicación."; -"shortcuts.edit.items.add_shortcut.caption" = "Añadir atajo"; - -/* MARK: PaywallView */ - -"paywall.title" = "Comprar"; -"paywall.sections.products.footer" = "Cada producto es una compra única y no recurrente. La compra de un proveedor no incluye una suscripción al servicio."; -"paywall.items.loading.caption" = "Cargando productos"; -"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 */ - -"donate.title" = "Donar"; -"donate.sections.one_time.header" = "Única"; -"donate.sections.one_time.footer" = "Si te gusta mi trabajo, aquí puedes colaborar con una donación.\n\nSólo se te cobrará una vez por donación, y puedes donar las veces que quieras."; -"donate.items.loading.caption" = "Cargando donaciones"; -"donate.items.purchasing.caption" = "Efectuando donación"; -"donate.alerts.purchase.success.message" = "Ésto significa mucho para mí y espero sinceramente que sigas usando y promoviendo esta aplicación."; -"donate.alerts.purchase.failure.message" = "Imposible completar la donación, por favor vuelve a intentarlo. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Ajustes"; -"settings.items.locks_in_background.caption" = "Bloquear acceso a la aplicación"; -"settings.items.donate.caption" = "Hacer una donación"; - -/* MARK: AboutView */ - -"about.title" = "Información"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Créditos"; -"about.items.join_community.caption" = "Apuntarse a la comunidad"; -"about.items.write_review.caption" = "Escribir una reseña"; -"about.items.share_twitter.caption" = "Enviar un Tweet!"; -"about.items.website.caption" = "Página de inicio"; -"about.items.disclaimer.caption" = "Aviso legal"; -"about.items.privacy_policy.caption" = "Política de privacidad"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Versión"; -"version.labels.intro" = "Passepartout y TunnelKit están escritos y son mantenidos por Davide De Rosa (keeshux).\n\nEl código de Passepartout y TunnelKit es público y está disponible en GitHub bajo la GPLv3, encontrarás enlaces en la página de inicio."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Créditos"; -"credits.sections.licenses.header" = "Licencias"; -"credits.sections.notices.header" = "Avisos"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Preferencias"; -"preferences.sections.general.header" = "General"; -"preferences.items.launches_on_login.caption" = "Iniciar al iniciar sesión"; -"preferences.items.launches_on_login.footer" = "Activa esta opción para que la aplicación se inicie automáticamente al iniciar o al iniciar sesión."; -"preferences.items.confirm_quit.caption" = "Confirmar salir"; -"preferences.items.confirm_quit.footer" = "Activa esta opción para que se muestre una alerta de confirmación al salir."; diff --git a/Passepartout/App/fastlane/ios/metadata/app_icon.jpg b/Passepartout/App/fastlane/ios/metadata/app_icon.jpg deleted file mode 100644 index ed687692..00000000 Binary files a/Passepartout/App/fastlane/ios/metadata/app_icon.jpg and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/metadata/de-DE/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/de-DE/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/de-DE/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/de-DE/name.txt b/Passepartout/App/fastlane/ios/metadata/de-DE/name.txt deleted file mode 100644 index 9bfb3d44..00000000 --- a/Passepartout/App/fastlane/ios/metadata/de-DE/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, VPN Client \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/de-DE/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/de-DE/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/de-DE/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/de-DE/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/de-DE/subtitle.txt deleted file mode 100644 index e57881c9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/de-DE/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Für WireGuard und OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/el/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/el/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/el/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/el/name.txt b/Passepartout/App/fastlane/ios/metadata/el/name.txt deleted file mode 100644 index 9bfb3d44..00000000 --- a/Passepartout/App/fastlane/ios/metadata/el/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, VPN Client \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/el/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/el/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/el/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/el/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/el/subtitle.txt deleted file mode 100644 index 0a0a3073..00000000 --- a/Passepartout/App/fastlane/ios/metadata/el/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Για WireGuard και OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/en-US/name.txt b/Passepartout/App/fastlane/ios/metadata/en-US/name.txt deleted file mode 100644 index 9bfb3d44..00000000 --- a/Passepartout/App/fastlane/ios/metadata/en-US/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, VPN Client \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/en-US/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/en-US/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/en-US/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/en-US/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/en-US/subtitle.txt deleted file mode 100644 index 0f289b20..00000000 --- a/Passepartout/App/fastlane/ios/metadata/en-US/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -For WireGuard and OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/es-MX/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/es-MX/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/es-MX/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/es-MX/name.txt b/Passepartout/App/fastlane/ios/metadata/es-MX/name.txt deleted file mode 100644 index 0af722d8..00000000 --- a/Passepartout/App/fastlane/ios/metadata/es-MX/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, Client VPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/es-MX/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/es-MX/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/es-MX/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/es-MX/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/es-MX/subtitle.txt deleted file mode 100644 index 5f6a8a9f..00000000 --- a/Passepartout/App/fastlane/ios/metadata/es-MX/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Para WireGuard y OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/fr-FR/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/fr-FR/apple_tv_privacy_policy.txt deleted file mode 100755 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/fr-FR/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/fr-FR/name.txt b/Passepartout/App/fastlane/ios/metadata/fr-FR/name.txt deleted file mode 100755 index 0af722d8..00000000 --- a/Passepartout/App/fastlane/ios/metadata/fr-FR/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, Client VPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/fr-FR/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/fr-FR/release_notes.txt deleted file mode 100755 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/fr-FR/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/fr-FR/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/fr-FR/subtitle.txt deleted file mode 100755 index 669ae18b..00000000 --- a/Passepartout/App/fastlane/ios/metadata/fr-FR/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Pour WireGuard et OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/it/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/it/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/it/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/it/name.txt b/Passepartout/App/fastlane/ios/metadata/it/name.txt deleted file mode 100644 index 0af722d8..00000000 --- a/Passepartout/App/fastlane/ios/metadata/it/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, Client VPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/it/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/it/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/it/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/it/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/it/subtitle.txt deleted file mode 100644 index 296a5bbb..00000000 --- a/Passepartout/App/fastlane/ios/metadata/it/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Per WireGuard ed OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/nl-NL/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/nl-NL/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/nl-NL/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/nl-NL/name.txt b/Passepartout/App/fastlane/ios/metadata/nl-NL/name.txt deleted file mode 100644 index 9bfb3d44..00000000 --- a/Passepartout/App/fastlane/ios/metadata/nl-NL/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, VPN Client \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/nl-NL/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/nl-NL/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/nl-NL/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/nl-NL/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/nl-NL/subtitle.txt deleted file mode 100644 index 11abb879..00000000 --- a/Passepartout/App/fastlane/ios/metadata/nl-NL/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Voor WireGuard en OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/pl/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/pl/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pl/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/pl/name.txt b/Passepartout/App/fastlane/ios/metadata/pl/name.txt deleted file mode 100644 index 198340f8..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pl/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, Klient VPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/pl/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/pl/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pl/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/pl/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/pl/subtitle.txt deleted file mode 100644 index 7245c98f..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pl/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Do WireGuard oraz OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/pt-BR/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/pt-BR/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pt-BR/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/pt-BR/name.txt b/Passepartout/App/fastlane/ios/metadata/pt-BR/name.txt deleted file mode 100644 index 0af722d8..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pt-BR/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, Client VPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/pt-BR/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/pt-BR/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pt-BR/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/pt-BR/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/pt-BR/subtitle.txt deleted file mode 100644 index 9b469f06..00000000 --- a/Passepartout/App/fastlane/ios/metadata/pt-BR/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Para WireGuard e OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/ru/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/ru/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/ru/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/ru/name.txt b/Passepartout/App/fastlane/ios/metadata/ru/name.txt deleted file mode 100644 index 8b798bd2..00000000 --- a/Passepartout/App/fastlane/ios/metadata/ru/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, VPN клиент \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/ru/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/ru/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/ru/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/ru/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/ru/subtitle.txt deleted file mode 100644 index 99a93bd8..00000000 --- a/Passepartout/App/fastlane/ios/metadata/ru/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -Для WireGuard и OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/sv/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/ios/metadata/sv/apple_tv_privacy_policy.txt deleted file mode 100644 index 8b137891..00000000 --- a/Passepartout/App/fastlane/ios/metadata/sv/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Passepartout/App/fastlane/ios/metadata/sv/name.txt b/Passepartout/App/fastlane/ios/metadata/sv/name.txt deleted file mode 100644 index 38d32c03..00000000 --- a/Passepartout/App/fastlane/ios/metadata/sv/name.txt +++ /dev/null @@ -1 +0,0 @@ -Passepartout, VPN Klient \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/metadata/sv/release_notes.txt b/Passepartout/App/fastlane/ios/metadata/sv/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/ios/metadata/sv/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/ios/metadata/sv/subtitle.txt b/Passepartout/App/fastlane/ios/metadata/sv/subtitle.txt deleted file mode 100644 index d92f1c71..00000000 --- a/Passepartout/App/fastlane/ios/metadata/sv/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -För WireGuard och OpenVPN \ No newline at end of file diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-01.png b/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-01.png deleted file mode 100644 index 1ae32be2..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-01.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-02.png b/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-02.png deleted file mode 100644 index bae4a504..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-02.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-03.png b/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-03.png deleted file mode 100644 index 9c16d144..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-03.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-04.png b/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-04.png deleted file mode 100644 index bf249193..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-04.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-05.png b/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-05.png deleted file mode 100644 index 4a250384..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/ipad-05.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-01.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-01.png deleted file mode 100644 index 0d8e793d..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-01.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-02.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-02.png deleted file mode 100644 index 2579b665..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-02.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-03.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-03.png deleted file mode 100644 index 83b0e37e..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-03.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-04.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-04.png deleted file mode 100644 index 9567cfe3..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-04.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-05.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-05.png deleted file mode 100644 index 651cca67..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone55-05.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-01.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-01.png deleted file mode 100644 index cc3b08b3..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-01.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-02.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-02.png deleted file mode 100644 index 0c1bb656..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-02.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-03.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-03.png deleted file mode 100644 index 0c846124..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-03.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-04.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-04.png deleted file mode 100644 index 3819b529..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-04.png and /dev/null differ diff --git a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-05.png b/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-05.png deleted file mode 100644 index 7f06cac0..00000000 Binary files a/Passepartout/App/fastlane/ios/screenshots/en-US/iphone65-05.png and /dev/null differ diff --git a/Passepartout/App/fastlane/mac/metadata/app_icon.jpg b/Passepartout/App/fastlane/mac/metadata/app_icon.jpg deleted file mode 100644 index b9f9b744..00000000 Binary files a/Passepartout/App/fastlane/mac/metadata/app_icon.jpg and /dev/null differ diff --git a/Passepartout/App/fastlane/mac/metadata/copyright.txt b/Passepartout/App/fastlane/mac/metadata/copyright.txt deleted file mode 120000 index ea7bc17b..00000000 --- a/Passepartout/App/fastlane/mac/metadata/copyright.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/copyright.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/description.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/keywords.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/name.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/de-DE/support_url.txt b/Passepartout/App/fastlane/mac/metadata/de-DE/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/de-DE/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/el/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/description.txt b/Passepartout/App/fastlane/mac/metadata/el/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/keywords.txt b/Passepartout/App/fastlane/mac/metadata/el/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/el/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/name.txt b/Passepartout/App/fastlane/mac/metadata/el/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/el/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/el/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/el/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/el/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/el/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/el/support_url.txt b/Passepartout/App/fastlane/mac/metadata/el/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/el/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/en-US/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/description.txt b/Passepartout/App/fastlane/mac/metadata/en-US/description.txt deleted file mode 120000 index 33981926..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/keywords.txt b/Passepartout/App/fastlane/mac/metadata/en-US/keywords.txt deleted file mode 120000 index d285c5ce..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/en-US/marketing_url.txt deleted file mode 120000 index 6bfb2aa4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/name.txt b/Passepartout/App/fastlane/mac/metadata/en-US/name.txt deleted file mode 120000 index 6c01feee..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/en-US/privacy_url.txt deleted file mode 120000 index 631e58f2..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/en-US/promotional_text.txt deleted file mode 120000 index 6d9f7336..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/en-US/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/en-US/subtitle.txt deleted file mode 120000 index 7a24133e..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/en-US/support_url.txt b/Passepartout/App/fastlane/mac/metadata/en-US/support_url.txt deleted file mode 120000 index 0076fdfd..00000000 --- a/Passepartout/App/fastlane/mac/metadata/en-US/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/description.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/keywords.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/name.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/es-MX/support_url.txt b/Passepartout/App/fastlane/mac/metadata/es-MX/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/es-MX/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/description.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/keywords.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/name.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/fr-FR/support_url.txt b/Passepartout/App/fastlane/mac/metadata/fr-FR/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/fr-FR/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/it/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/description.txt b/Passepartout/App/fastlane/mac/metadata/it/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/keywords.txt b/Passepartout/App/fastlane/mac/metadata/it/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/it/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/name.txt b/Passepartout/App/fastlane/mac/metadata/it/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/it/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/it/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/it/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/it/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/it/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/it/support_url.txt b/Passepartout/App/fastlane/mac/metadata/it/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/it/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/description.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/keywords.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/name.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/nl-NL/support_url.txt b/Passepartout/App/fastlane/mac/metadata/nl-NL/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/nl-NL/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/pl/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/description.txt b/Passepartout/App/fastlane/mac/metadata/pl/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/keywords.txt b/Passepartout/App/fastlane/mac/metadata/pl/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/pl/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/name.txt b/Passepartout/App/fastlane/mac/metadata/pl/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/pl/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/pl/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/pl/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/pl/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/pl/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pl/support_url.txt b/Passepartout/App/fastlane/mac/metadata/pl/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pl/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/primary_category.txt b/Passepartout/App/fastlane/mac/metadata/primary_category.txt deleted file mode 120000 index 53417145..00000000 --- a/Passepartout/App/fastlane/mac/metadata/primary_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/primary_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/primary_first_sub_category.txt b/Passepartout/App/fastlane/mac/metadata/primary_first_sub_category.txt deleted file mode 120000 index f671236a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/primary_first_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/primary_first_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/primary_second_sub_category.txt b/Passepartout/App/fastlane/mac/metadata/primary_second_sub_category.txt deleted file mode 120000 index a94c03a0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/primary_second_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/primary_second_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/description.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/keywords.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/name.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/pt-BR/support_url.txt b/Passepartout/App/fastlane/mac/metadata/pt-BR/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/pt-BR/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/ru/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/description.txt b/Passepartout/App/fastlane/mac/metadata/ru/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/keywords.txt b/Passepartout/App/fastlane/mac/metadata/ru/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/ru/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/name.txt b/Passepartout/App/fastlane/mac/metadata/ru/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/ru/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/ru/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/ru/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/ru/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/ru/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/ru/support_url.txt b/Passepartout/App/fastlane/mac/metadata/ru/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/ru/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/secondary_category.txt b/Passepartout/App/fastlane/mac/metadata/secondary_category.txt deleted file mode 120000 index c45a96c9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/secondary_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/secondary_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/secondary_first_sub_category.txt b/Passepartout/App/fastlane/mac/metadata/secondary_first_sub_category.txt deleted file mode 120000 index 99aef6d9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/secondary_first_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/secondary_first_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/secondary_second_sub_category.txt b/Passepartout/App/fastlane/mac/metadata/secondary_second_sub_category.txt deleted file mode 120000 index 289d86cf..00000000 --- a/Passepartout/App/fastlane/mac/metadata/secondary_second_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/secondary_second_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/mac/metadata/sv/apple_tv_privacy_policy.txt deleted file mode 120000 index 3dbe428c..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/description.txt b/Passepartout/App/fastlane/mac/metadata/sv/description.txt deleted file mode 120000 index 47aa7794..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/description.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/description.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/keywords.txt b/Passepartout/App/fastlane/mac/metadata/sv/keywords.txt deleted file mode 120000 index b49f15e0..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/marketing_url.txt b/Passepartout/App/fastlane/mac/metadata/sv/marketing_url.txt deleted file mode 120000 index e829db4d..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/name.txt b/Passepartout/App/fastlane/mac/metadata/sv/name.txt deleted file mode 120000 index d7c844b7..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/privacy_url.txt b/Passepartout/App/fastlane/mac/metadata/sv/privacy_url.txt deleted file mode 120000 index 94cbbd7a..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/promotional_text.txt b/Passepartout/App/fastlane/mac/metadata/sv/promotional_text.txt deleted file mode 120000 index c6cda2f4..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/release_notes.txt b/Passepartout/App/fastlane/mac/metadata/sv/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/mac/metadata/sv/subtitle.txt b/Passepartout/App/fastlane/mac/metadata/sv/subtitle.txt deleted file mode 120000 index ec2c7096..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/metadata/sv/support_url.txt b/Passepartout/App/fastlane/mac/metadata/sv/support_url.txt deleted file mode 120000 index 77f602ae..00000000 --- a/Passepartout/App/fastlane/mac/metadata/sv/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/mac/screenshots/en-US/mac-01.png b/Passepartout/App/fastlane/mac/screenshots/en-US/mac-01.png deleted file mode 100644 index 2fe4c9ad..00000000 Binary files a/Passepartout/App/fastlane/mac/screenshots/en-US/mac-01.png and /dev/null differ diff --git a/Passepartout/App/fastlane/mac/screenshots/en-US/mac-02.png b/Passepartout/App/fastlane/mac/screenshots/en-US/mac-02.png deleted file mode 100644 index 8b93a5ba..00000000 Binary files a/Passepartout/App/fastlane/mac/screenshots/en-US/mac-02.png and /dev/null differ diff --git a/Passepartout/App/fastlane/mac/screenshots/en-US/mac-03.png b/Passepartout/App/fastlane/mac/screenshots/en-US/mac-03.png deleted file mode 100644 index d965b83a..00000000 Binary files a/Passepartout/App/fastlane/mac/screenshots/en-US/mac-03.png and /dev/null differ diff --git a/Passepartout/App/fastlane/symlink-appletv-privacy.sh b/Passepartout/App/fastlane/symlink-appletv-privacy.sh deleted file mode 100755 index d738c9be..00000000 --- a/Passepartout/App/fastlane/symlink-appletv-privacy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -LANGUAGE=$1 -FILENAME="apple_tv_privacy_policy.txt" -IOS_DIR=../../../ios/metadata/en-US -cd $LANGUAGE -rm -f $FILENAME -ln -s "$IOS_DIR/$FILENAME" diff --git a/Passepartout/App/fastlane/symlink-l10n.sh b/Passepartout/App/fastlane/symlink-l10n.sh deleted file mode 100755 index 72234922..00000000 --- a/Passepartout/App/fastlane/symlink-l10n.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -LANGUAGE=$1 -FILELIST="apple_tv_privacy_policy.txt description.txt keywords.txt marketing_url.txt name.txt privacy_url.txt promotional_text.txt subtitle.txt support_url.txt" -IOS_DIR=../../../ios/metadata -cd $LANGUAGE -rm *.txt -for FILENAME in $FILELIST; do - ln -s "$IOS_DIR/$LANGUAGE/$FILENAME" -done diff --git a/Passepartout/App/fastlane/tvos/metadata/copyright.txt b/Passepartout/App/fastlane/tvos/metadata/copyright.txt deleted file mode 120000 index ea7bc17b..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/copyright.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/copyright.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/description.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/description.txt deleted file mode 100644 index f63a1185..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout ist ein smarter VPN client der perfekt in die Apple-Plattform integriert ist. Dank der Ende-zu-Ende-Verschlüsselung des CloudKits kannst du die VPN-Profile deiner iOS- und macOS-Apps jetzt auch sicher mit deinem Apple TV teilen. - -Passepartout ist Open Source: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/keywords.txt deleted file mode 120000 index 4d587f18..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/marketing_url.txt deleted file mode 120000 index 641438ba..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/name.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/name.txt deleted file mode 120000 index 768f56fb..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/privacy_url.txt deleted file mode 120000 index a4a7a52c..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/promotional_text.txt deleted file mode 120000 index 2dc0ca16..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/subtitle.txt deleted file mode 120000 index 13ed3c40..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/de-DE/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/de-DE/support_url.txt deleted file mode 120000 index adcdde2b..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/de-DE/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/de-DE/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/el/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/description.txt b/Passepartout/App/fastlane/tvos/metadata/el/description.txt deleted file mode 100644 index 83832184..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Το Passepartout είναι ένας έξυπνος πελάτης VPN που είναι απόλυτα ενσωματωμένος στις πλατφόρμες Apple. Μπορείτε πλέον να μοιράζεστε τα προφίλ σας VPN από τις εφαρμογές iOS και macOS με το Apple TV σας, επίσης με ασφάλεια χάρη στην κρυπτογράφηση από άκρο σε άκρο του CloudKit. - -To Passepartout βασίζεται σε ανοιχτό κώδικα: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/el/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/el/keywords.txt deleted file mode 120000 index fcb4a48d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/el/marketing_url.txt deleted file mode 120000 index 389a9a0a..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/name.txt b/Passepartout/App/fastlane/tvos/metadata/el/name.txt deleted file mode 120000 index f3df0704..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/el/privacy_url.txt deleted file mode 120000 index 39ac401f..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/el/promotional_text.txt deleted file mode 120000 index 1f79def5..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/el/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/el/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/el/subtitle.txt deleted file mode 120000 index 1151d981..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/el/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/el/support_url.txt deleted file mode 120000 index 7824c415..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/el/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/el/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/description.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/description.txt deleted file mode 100644 index 0bf5a40d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout is a smart VPN client perfectly integrated with Apple platforms. You may now share your VPN profiles from the iOS and macOS apps with your Apple TV, also safely thanks to CloudKit end-to-end encryption. - -Passepartout is open source: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/keywords.txt deleted file mode 120000 index d285c5ce..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/marketing_url.txt deleted file mode 120000 index 6bfb2aa4..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/name.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/name.txt deleted file mode 120000 index 6c01feee..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/privacy_url.txt deleted file mode 120000 index 631e58f2..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/promotional_text.txt deleted file mode 120000 index 6d9f7336..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/subtitle.txt deleted file mode 120000 index 7a24133e..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/en-US/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/en-US/support_url.txt deleted file mode 120000 index 0076fdfd..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/en-US/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/description.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/description.txt deleted file mode 100644 index 377f9e0d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout es un cliente VPN inteligente perfectamente integrado con las plataformas Apple. Ya puedes compartir tus perfiles VPN desde tus apps iOS y macOS con tu Apple TV, también de forma segura gracias a la criptografía end-to-end de CloudKit. - -Passepartout tiene código abierto: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/keywords.txt deleted file mode 120000 index 01ac2936..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/marketing_url.txt deleted file mode 120000 index e9f02a42..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/name.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/name.txt deleted file mode 120000 index c4ffda47..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/privacy_url.txt deleted file mode 120000 index f0b4717c..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/promotional_text.txt deleted file mode 120000 index 43a77163..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/subtitle.txt deleted file mode 120000 index a5f53378..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/es-MX/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/es-MX/support_url.txt deleted file mode 120000 index 35263621..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/es-MX/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/es-MX/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/description.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/description.txt deleted file mode 100644 index acb361f4..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout est un client VPN parfaitement intégré avec les plateformes Apple. Vous pouvez à présent partager vos profils VPN depuis les applications iOS et macOS avec votre Apple TV en toute sécurité grâce au chiffrement de bout en bout CloudKit. - -Passepartout est open source: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/keywords.txt deleted file mode 120000 index 3257ef14..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/marketing_url.txt deleted file mode 120000 index 86e000c3..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/name.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/name.txt deleted file mode 120000 index 86751c5b..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/privacy_url.txt deleted file mode 120000 index 91460c59..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/promotional_text.txt deleted file mode 120000 index 4b471308..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/subtitle.txt deleted file mode 120000 index 91e54eed..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/fr-FR/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/fr-FR/support_url.txt deleted file mode 120000 index e826cede..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/fr-FR/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/fr-FR/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/it/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/description.txt b/Passepartout/App/fastlane/tvos/metadata/it/description.txt deleted file mode 100644 index d60e6698..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout è un client VPN intelligente perfettamente integrato con le piattaforme Apple. Ora puoi condividere i tuoi profili VPN dalle app iOS e macOS con la tua Apple TV, e in modo sicuro grazie alla crittografia end-to-end di CloudKit. - -Passepartout è open source: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/it/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/it/keywords.txt deleted file mode 120000 index 2fd1fbd0..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/it/marketing_url.txt deleted file mode 120000 index 0753c8ea..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/name.txt b/Passepartout/App/fastlane/tvos/metadata/it/name.txt deleted file mode 120000 index a5af425b..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/it/privacy_url.txt deleted file mode 120000 index f978fbf1..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/it/promotional_text.txt deleted file mode 120000 index 6f450d39..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/it/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/it/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/it/subtitle.txt deleted file mode 120000 index f7672a65..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/it/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/it/support_url.txt deleted file mode 120000 index 64476e81..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/it/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/it/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/description.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/description.txt deleted file mode 100644 index a893d742..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout is een slimme VPN-client die perfect is geïntegreerd in Apple-platformen. Je kunt je VPN-profielen van de iOS- en macOS-apps nu delen met je Apple TV, ook op een veilige manier dankzij de volledige encryptie van CloudKit. - -Passepartout is open source: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/keywords.txt deleted file mode 120000 index bcaebd4e..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/marketing_url.txt deleted file mode 120000 index 1f730d5d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/name.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/name.txt deleted file mode 120000 index 2876284f..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/privacy_url.txt deleted file mode 120000 index 5f224f28..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/promotional_text.txt deleted file mode 120000 index efc3e72c..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/subtitle.txt deleted file mode 120000 index 002950a4..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/nl-NL/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/nl-NL/support_url.txt deleted file mode 120000 index ed8b9e79..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/nl-NL/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/nl-NL/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/pl/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/description.txt b/Passepartout/App/fastlane/tvos/metadata/pl/description.txt deleted file mode 100644 index 4b892802..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout jest inteligentnym klientem VPN, perfekcyjnie zintegrowanym z platformami systemu iOS. Możesz teraz udostępniać swoje profile VPN z aplikacji iOS i macOS na Apple TV, również bezpiecznie dzięki szyfrowaniu typu end-to-end CloudKit. - -Passepartout jest aplikacją typu open source: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/pl/keywords.txt deleted file mode 120000 index 071cd410..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/pl/marketing_url.txt deleted file mode 120000 index f09f7b45..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/name.txt b/Passepartout/App/fastlane/tvos/metadata/pl/name.txt deleted file mode 120000 index 4cad052e..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/pl/privacy_url.txt deleted file mode 120000 index 6d552b46..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/pl/promotional_text.txt deleted file mode 120000 index 205f560b..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/pl/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/pl/subtitle.txt deleted file mode 120000 index fef06411..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pl/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/pl/support_url.txt deleted file mode 120000 index f252f8f9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pl/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pl/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/primary_category.txt b/Passepartout/App/fastlane/tvos/metadata/primary_category.txt deleted file mode 120000 index 53417145..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/primary_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/primary_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/primary_first_sub_category.txt b/Passepartout/App/fastlane/tvos/metadata/primary_first_sub_category.txt deleted file mode 120000 index f671236a..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/primary_first_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/primary_first_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/primary_second_sub_category.txt b/Passepartout/App/fastlane/tvos/metadata/primary_second_sub_category.txt deleted file mode 120000 index a94c03a0..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/primary_second_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/primary_second_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/description.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/description.txt deleted file mode 100644 index 518fea21..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout é um cliente inteligente de VPN perfeitamente integrado com Apple. Agora pode partilhar os seus perfis VPN a partir de aplicações iOS e macOS com a sua Apple TV, também de forma segura graças à encriptação completa da CloudKit. - -Passepartout possui código aberto: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/keywords.txt deleted file mode 120000 index 1320138f..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/marketing_url.txt deleted file mode 120000 index cffad23d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/name.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/name.txt deleted file mode 120000 index 57d9b60e..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/privacy_url.txt deleted file mode 120000 index 8121e236..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/promotional_text.txt deleted file mode 120000 index 0219a69c..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/subtitle.txt deleted file mode 120000 index 952b8b94..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/pt-BR/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/pt-BR/support_url.txt deleted file mode 120000 index 377fefd6..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/pt-BR/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/pt-BR/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/ru/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/description.txt b/Passepartout/App/fastlane/tvos/metadata/ru/description.txt deleted file mode 100644 index 9652e86f..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout — это умный VPN-клиент, идеально интегрированный с платформами Apple. Теперь вы можете безопасно делиться своими профилями VPN из приложений iOS и macOS на Apple TV благодаря сквозному шифрованию CloudKit. - -У Passepartout открытый исходный код: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/ru/keywords.txt deleted file mode 120000 index 77caf16d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/ru/marketing_url.txt deleted file mode 120000 index 8f4a4555..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/name.txt b/Passepartout/App/fastlane/tvos/metadata/ru/name.txt deleted file mode 120000 index 95a91e6f..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/ru/privacy_url.txt deleted file mode 120000 index 15aa6090..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/ru/promotional_text.txt deleted file mode 120000 index 4dfc0690..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/ru/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/ru/subtitle.txt deleted file mode 120000 index 04a61e98..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/ru/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/ru/support_url.txt deleted file mode 120000 index fce8a988..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/ru/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/ru/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/secondary_category.txt b/Passepartout/App/fastlane/tvos/metadata/secondary_category.txt deleted file mode 120000 index c45a96c9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/secondary_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/secondary_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/secondary_first_sub_category.txt b/Passepartout/App/fastlane/tvos/metadata/secondary_first_sub_category.txt deleted file mode 120000 index 99aef6d9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/secondary_first_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/secondary_first_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/secondary_second_sub_category.txt b/Passepartout/App/fastlane/tvos/metadata/secondary_second_sub_category.txt deleted file mode 120000 index 289d86cf..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/secondary_second_sub_category.txt +++ /dev/null @@ -1 +0,0 @@ -../../ios/metadata/secondary_second_sub_category.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/apple_tv_privacy_policy.txt b/Passepartout/App/fastlane/tvos/metadata/sv/apple_tv_privacy_policy.txt deleted file mode 120000 index db021f29..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/apple_tv_privacy_policy.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/en-US/apple_tv_privacy_policy.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/description.txt b/Passepartout/App/fastlane/tvos/metadata/sv/description.txt deleted file mode 100644 index 50c21dd0..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/description.txt +++ /dev/null @@ -1,3 +0,0 @@ -Passepartout är en smart VPN-klient som är helt integrerad med Apples plattformar. Du kan nu dela dina VPN-profiler från iOS- och macOS-apparna med din Apple TV på ett säkert sätt tack vare end-to-end-kryptering med CloudKit. - -Passepartout är öppen källkod: https://github.com/passepartoutvpn diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/keywords.txt b/Passepartout/App/fastlane/tvos/metadata/sv/keywords.txt deleted file mode 120000 index 09c86041..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/keywords.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/keywords.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/marketing_url.txt b/Passepartout/App/fastlane/tvos/metadata/sv/marketing_url.txt deleted file mode 120000 index b5ad9e05..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/marketing_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/name.txt b/Passepartout/App/fastlane/tvos/metadata/sv/name.txt deleted file mode 120000 index e660818d..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/name.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/name.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/privacy_url.txt b/Passepartout/App/fastlane/tvos/metadata/sv/privacy_url.txt deleted file mode 120000 index 7d9ea64e..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/privacy_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/promotional_text.txt b/Passepartout/App/fastlane/tvos/metadata/sv/promotional_text.txt deleted file mode 120000 index b6958df6..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/promotional_text.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/release_notes.txt b/Passepartout/App/fastlane/tvos/metadata/sv/release_notes.txt deleted file mode 100644 index 522c05a9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/release_notes.txt +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed - -- Restricted profile not updated. -- Selection and switch have the same color in organizer. - diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/subtitle.txt b/Passepartout/App/fastlane/tvos/metadata/sv/subtitle.txt deleted file mode 120000 index 0c89fc49..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/subtitle.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/metadata/sv/support_url.txt b/Passepartout/App/fastlane/tvos/metadata/sv/support_url.txt deleted file mode 120000 index 502dfbe9..00000000 --- a/Passepartout/App/fastlane/tvos/metadata/sv/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -../../../ios/metadata/sv/support_url.txt \ No newline at end of file diff --git a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-01.png b/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-01.png deleted file mode 100644 index 51f773d2..00000000 Binary files a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-01.png and /dev/null differ diff --git a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-02.png b/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-02.png deleted file mode 100644 index 7f17966b..00000000 Binary files a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-02.png and /dev/null differ diff --git a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-03.png b/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-03.png deleted file mode 100644 index 65d4c267..00000000 Binary files a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-03.png and /dev/null differ diff --git a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-04.png b/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-04.png deleted file mode 100644 index 6d84dd78..00000000 Binary files a/Passepartout/App/fastlane/tvos/screenshots/en-US/tv-04.png and /dev/null differ diff --git a/Passepartout/App/fr.lproj/InfoPlist.strings b/Passepartout/App/fr.lproj/InfoPlist.strings deleted file mode 100644 index 54ff6ab2..00000000 --- a/Passepartout/App/fr.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Nom d'accès au présent réseau Wi-Fi"; - -"NSFaceIDUsageDescription" = "Déverrouiller l'application avec Face ID"; diff --git a/Passepartout/App/fr.lproj/Localizable.strings b/Passepartout/App/fr.lproj/Localizable.strings deleted file mode 100644 index c1c36e1c..00000000 --- a/Passepartout/App/fr.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Politique"; -"global.strings.networks" = "Réseaux"; -"global.strings.edit" = "Modifier"; -"global.strings.unknown" = "Inconnu"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Veuillez sélectionner un point terminal"; - -"on_demand.sections.policy.footer.any" = "sur n'importe quel réseau"; -"on_demand.sections.policy.footer.including" = "sur les réseaux ci-dessous uniquement"; -"on_demand.sections.policy.footer.excluding" = "sauf sur les réseaux ci-dessous"; -"on_demand.policy.any" = "Tous les réseaux"; -"on_demand.policy.including" = "Inclure"; -"on_demand.policy.excluding" = "Exclure"; - -"settings.sections.icloud.footer" = "Désactiver la synchronisation pour effacer. Pour effacer la boutique iCloud en toute sécurité, faites de même sur tous vos appareils synchronisés. Ceci n'affectera pas les profils locaux."; -"settings.items.should_enable_cloud_syncing.caption" = "Synchroniser avec iCloud"; -"settings.items.erase_cloud_store.caption" = "Effacer la boutique iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Annuler"; -"global.strings.next" = "Suivant"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Enregistrer"; -"global.strings.rename" = "Renommer"; -"global.strings.duplicate" = "Dupliquer"; -"global.strings.add" = "Ajouter"; -"global.strings.delete" = "Supprimer"; -"global.strings.uninstall" = "Désinstaller"; -"global.strings.default" = "Default"; -"global.strings.name" = "Nom"; -"global.strings.profiles" = "Profils"; -"global.strings.provider" = "Fournisseur"; -"global.strings.providers" = "Fournisseurs"; -"global.strings.configuration" = "Configuration"; -"global.strings.address" = "Adresse"; -"global.strings.addresses" = "Adresses"; -"global.strings.port" = "Port"; -"global.strings.protocol" = "Protocole"; -"global.strings.protocols" = "Protocols"; -"global.strings.enabled" = "Activer"; -"global.strings.disabled" = "Désactiver"; -"global.strings.none" = "Aucun"; -"global.strings.automatic" = "Automatique"; -"global.strings.manual" = "Manuel"; -"global.strings.encryption" = "Cryptage"; -"global.strings.reconnect" = "Reconnecter"; -"global.strings.servers" = "Serveurs"; -"global.strings.domain" = "Domaine"; -"global.strings.domains" = "Domaines"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interface"; -"global.strings.private_key" = "Clé privée"; -"global.strings.public_key" = "Clé publique"; -"global.strings.endpoint" = "Point terminal"; -"global.strings.keepalive" = "Garder actif"; -"global.strings.advanced" = "Avancé"; -"global.strings.translations" = "Traductions"; -"global.strings.show" = "Afficher"; -"global.strings.connect" = "Connecter"; -"global.strings.disconnect" = "Déconnecter"; -"global.strings.download" = "Télécharger"; -"global.strings.authentication" = "Authentification"; -"global.messages.unlock_app" = "Passepartout verrouillé"; -"global.messages.email_not_configured" = "Aucun compte courriel n'est configuré."; -"global.messages.share" = "Passepartout est un client OpenVPN / WireGuard simple d'utilisation et open source pour iOS et macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Me rappeler plus tard"; -"global.alerts.buttons.never" = "Ne pas me redemander"; - -"global.placeholders.profile_name" = "Mon profile"; - -"global.errors.missing_profile" = "Profil manquant"; -"global.errors.missing_account" = "Compte manquant"; -"global.errors.missing_provider_server" = "Emplacement manquant"; -"global.errors.missing_provider_preset" = "Préréglage manquant"; -"global.errors.tunnel_expired" = "Connexion expirée"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Support"; -"menu.all.share.title" = "Partager"; -"menu.all.about.title" = "À propos %@"; - -"menu.contextual.add_profile.from_files" = "De Fichiers"; -"menu.contextual.add_profile.from_text" = "Du Texte"; -"menu.contextual.add_profile.imported" = "Ajouter %@"; -"menu.contextual.support.write_review" = "Avis"; -"menu.contextual.support.join_community" = "Communité"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Inviter"; - -"menu.system.quit.title" = "Quitter %@"; - -"menu.system.quit.messages.confirm" = "S'il est activé, le VPN fonctionnera en tâche de fond. Voulez-vous quitter ?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Connection..."; -"tunnelkit.vpn.active" = "Actif"; -"tunnelkit.vpn.disconnecting" = "Déconnection..."; -"tunnelkit.vpn.inactive" = "Inactif"; -"tunnelkit.vpn.disabled" = "Désactivé"; -"tunnelkit.vpn.unused" = "Désactivé"; - -"tunnelkit.errors.vpn.timeout" = "Délais dépassé"; -"tunnelkit.errors.vpn.dns" = "Échec DNS"; -"tunnelkit.errors.vpn.auth" = "Échec Auth"; -"tunnelkit.errors.vpn.tls" = "Échec TLS"; -"tunnelkit.errors.vpn.encryption" = "Échec du cryptage"; -"tunnelkit.errors.vpn.compression" = "Compression non supportée"; -"tunnelkit.errors.vpn.network" = "Réseau modifié"; -"tunnelkit.errors.vpn.routing" = "Routage manquant"; -"tunnelkit.errors.vpn.gateway" = "Aucune passerelle"; -"tunnelkit.errors.vpn.shutdown" = "Arrêt du serveur"; - -"tunnelkit.errors.parsing" = "Incapable d'analyser le fichier de configuration fournis. (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Le fichier de configuration contient une mauvaise option.(%@)."; -"tunnelkit.errors.openvpn.required_option" = "Le fichier de configuration ne contient pas une option requise. (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Le fichier de configuration contient une option non supportée (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Le fichier de configuration est adéquat, mais contient une option potentiellement non supportée. (%@).\n\nLa connection peut être perdue selon les paramètres du serveur."; -"tunnelkit.errors.openvpn.passphrase_required" = "Veuillez entrer le mot de passe d'encryption."; -"tunnelkit.errors.openvpn.decryption" = "Le fichier de configuration contient une clé privée encryptée et n'a pas été décryptée. Veuillez revérifier votre mot de passe."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "En utilisation"; -"organizer.sections.tv.profiles_list.header.p1" = "Ouvrez Passepartout sur votre appareil iOS ou macOS et activez la bascule « Apple TV » d'un profil pour le faire apparaître ici."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Aucun profil"; - -"organizer.alerts.reddit.message" = "Saviez-vous que Passepartout a un subreddit? Souscrivez pour les mises à jour ou discuter des problèmes, caractéristiques, nouvelles plateformes ou quoi que ce soit.\n\nC'est aussi une très bonne façon de démontrer votre enthousiasme envers le projet."; -"organizer.alerts.reddit.buttons.subscribe" = "Souscrivez maintenant!"; - -"organizer.alerts.remove_profile.message" = "Voulez-vous vraiment supprimer le profil %@ ?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Nouveau profil"; -"add_profile.shared.views.existing.header" = "Profiles existants"; -"add_profile.shared.alerts.overwrite.message" = "Un profile avec ce même nom existe déjà. Le remplacer ?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Saisir le mot de passe"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Ici vous pouvez trousers certains fournisseurs avec des profiles déjà configurés."; -"add_profile.provider.items.update_list" = "Actualiser la liste"; -"add_profile.provider.errors.no_default_server" = "Aucun serveur trouvé"; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Bienvenue à Passepartout!\n\nUtilisez l'organiseur pour ajouter un nouveau profile."; - -"profile.sections.vpn.footer" = "La connection sera établie lorsque nécessaire."; -"profile.sections.status.header" = "Connection"; -"profile.sections.provider_infrastructure.footer" = "Mis à jour : %@."; -"profile.sections.tv.footer.encryption" = "Les profils sont cryptés et rendus disponible pour votre Apple TV via iCloud."; -"profile.sections.tv.footer.restricted.p1" = "Cependant, la connexion expirera après %d minutes."; -"profile.sections.tv.footer.restricted.p2" = "Achetez pour lever la restriction."; -"profile.sections.vpn_survives_sleep.footer" = "Désactiver pour augmenter l'autonomie de la batterie, au dépends de la rapidité au réveil pour la reconnection."; -"profile.sections.vpn_resolves_hostname.footer" = "Préféré dans la plus part des réseaux et requis dans certains réseaux IPv6. Désactiver lorsque le DNS est bloqué ou pour augmenter la rapidité des négociations lorsque le DNS est lent à répondre."; -"profile.sections.feedback.header" = "Commentaires"; -"profile.items.use_profile.caption" = "Utiliser ce profile"; -"profile.items.vpn_service.caption" = "Activer"; -"profile.items.vpn.turn_on.caption" = "Activer VPN"; -"profile.items.vpn.turn_off.caption" = "Désactiver VPN"; -"profile.items.connection_status.caption" = "Statut"; -"profile.items.data_count.caption" = "Échanger les données"; -"profile.items.randomizes_server.caption" = "Rendre aléatoire le serveur"; -"profile.items.provider.refresh.caption" = "Rafraîchir l'infrastructure"; -"profile.items.category.caption" = "Catégorie"; -"profile.items.only_shows_favorites.caption" = "Afficher uniquement les emplacements favoris"; -"profile.items.vpn_survives_sleep.caption" = "Garder actif lors de la veille"; -"profile.items.vpn_resolves_hostname.caption" = "Résoudre le nom d'hôte du serveur"; -"profile.items.tv_sharing.caption.limited" = "Limité à %d minutes"; -"profile.items.expires_at.caption" = "Expiration"; - -"profile.alerts.rename.title" = "Renommer le profile"; -"profile.alerts.reconnect_vpn.message" = "Voulez-vous reconnecter le VPN?"; -"profile.alerts.uninstall_vpn.message" = "Voulez-vous vraiment effacer la configuration VPN de vos paramètres? Ceci peux fixer certains VPN en arrêt et n'affectera pas vos profiles de fournisseurs et hôtes."; -"profile.alerts.test_connectivity.title" = "Connections"; -"profile.alerts.test_connectivity.messages.success" = "Votre appareil est connecté à Internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Votre appareil n'a aucune connection Invernet, veuillez vérifier vos paramètres de profile."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Locallisation"; -"provider.location.sections.empty_favorites.footer" = "Glissez vers la gauche d'un item pour l'ajouter ou le retirer des Favoris."; -"provider.location.actions.favorite" = "Favoris"; -"provider.location.actions.unfavorite" = "Retirer des Favoris"; - -"provider.preset.title" = "Préréglage"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Compte"; -"account.sections.credentials.header" = "Indetifiants"; -"account.sections.registration.footer" = "Allez créer un compte sur le site %@."; -"account.items.authentication_method.persistent" = "Persistent"; -"account.items.authentication_method.interactive" = "Interactif"; -"account.items.username.caption" = "Nom d'utilisateur"; -"account.items.username.placeholder" = "nom d'utilisateur"; -"account.items.password.caption" = "Mot de passe"; -"account.items.password.placeholder" = "secret"; -"account.items.seed.caption" = "Source"; -"account.items.open_guide.caption" = "Voir vos identifiants"; -"account.items.signup.caption" = "S'inscrire avec %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Utilisez votre identifiants web de %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Utilisez vos informations d'identification de service %@, qui peuvent différer des informations d'identification du web."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement numérique (sans espaces)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement votre courriel."; -"account.sections.guidance.footer.infrastructure.pia" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement numérique avec le préfixe \"p\" "; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Trouvez votre identifiant web %@ dans la section du site web \"Account > OpenVPN / IKEv2 nom d'utilisateur\" "; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement votre courriel."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Utilisez votre identifiants web de %@. Votre nom d'utilisateur est normalement votre courriel."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Trouver votre identifiant %@ dans la section web Générateur de configuration OpenVPN."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Clé pré-partagée"; -"endpoint.wireguard.items.allowed_ip.caption" = "IP autorisée"; - -"endpoint.advanced.title" = "Détails techniques"; -"endpoint.advanced.openvpn.sections.pull.header" = "Tirer du serveur"; -"endpoint.advanced.openvpn.sections.communication.header" = "Communications"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Si vous obtenez une connection erronnée après le changement des parameters de communication, tapotez pour revenir à la configuration initiale."; -"endpoint.advanced.openvpn.sections.compression.header" = "Compression"; -"endpoint.advanced.openvpn.sections.network.header" = "Réseau"; -"endpoint.advanced.openvpn.sections.other.header" = "Autre"; -"endpoint.advanced.openvpn.items.route.caption" = "Routage"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cryptogramme"; -"endpoint.advanced.openvpn.items.digest.caption" = "Authentification"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Intégré"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algorithme"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Non supporté"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Réinitialiser la configuration"; -"endpoint.advanced.openvpn.items.client.caption" = "Certificat"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Clé"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Verifié"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Non vérifié"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Authentification"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Cryptage"; -"endpoint.advanced.openvpn.items.eku.caption" = "Vérification étendue"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d secondes"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Renégociation"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "aprè %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Extrémité aléatoire"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Rendre aléatoire les noms d'hôtes"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Paramètres réseaux"; -"network_settings.sections.choices.header" = "Écraser"; -"network_settings.gateway.title" = "Gateway"; -"network_settings.proxy.items.bypass_domains.caption" = "Outrepasser le domaine"; -"network_settings.items.add_dns_server.caption" = "Ajouter une adresse"; -"network_settings.items.add_dns_domain.caption" = "Ajouter un domaine"; -"network_settings.items.proxy_bypass.caption" = "Outrepasser le domaine"; -"network_settings.items.add_proxy_bypass.caption" = "Ajouter outrepasser le domaine"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "Sur demande"; -"on_demand.sections.policy.footer" = "Activez le VPN %@."; -"on_demand.items.add_ssid.caption" = "Ajouter Wi-Fi"; -"on_demand.items.active.caption" = "Fiables"; -"on_demand.items.mobile.caption" = "Réseau cellulaire"; -"on_demand.items.ethernet.caption" = "Connexions filaires"; - -"on_demand.items.policy.caption" = "La confiance désactive le VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnostiques"; -"diagnostics.sections.debug_log.footer" = "Camouflage du status sera effectif après la reconnection. Les données réseaux sont les noms d'hôtes, adresses IP, routage, SSID. Les identifiants et clés privés ne sont pas enregistrés."; -"diagnostics.items.server_configuration.caption" = "Configuration serveur"; -"diagnostics.items.app_log.title" = "Application"; -"diagnostics.items.masks_private_data.caption" = "Masquer les données de réseau"; -"diagnostics.items.report_issue.caption" = "Rapporter un problème de connection"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Pour bien réinitialiser le registre de diagnostique et appliquer les préférences de camouflage, vous devez vous reconnecter au VPN maintenant."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Journal de débogage"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Copier"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Rapporter un problème"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Ajouter un raccourcis"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Cellulaire"; -"shortcuts.add.items.connect.caption" = "Connecter à"; -"shortcuts.add.items.enable_vpn.caption" = "Activer VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Désactiver VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Faire confiance au présent réseau Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Retirer le présent réseau Wi-Fi des réseaux de confiance."; -"shortcuts.add.items.trust_cellular.caption" = "Faire confiance au présent réseau cellulaire"; -"shortcuts.add.items.untrust_cellular.caption" = "Retirer le présent réseau cellulaire des réseaux de confiance."; -"shortcuts.add.alerts.no_profiles.message" = "Il n'y a aucun profile pour se connecter."; - -"shortcuts.edit.title" = "Gérer les raccourcis"; -"shortcuts.edit.sections.all.header" = "Raccourcis existants"; -"shortcuts.edit.sections.add.footer" = "Obtenez de l'aide de Siri pour accélérer vos intéractions les plus courantes avec l'app."; -"shortcuts.edit.items.add_shortcut.caption" = "Ajouter un raccourcis"; - -/* MARK: PaywallView */ - -"paywall.title" = "Acheter"; -"paywall.sections.products.footer" = "Chaque produit est un achat unique. Les achats n'incluent pas une souscription à un service de VPN."; -"paywall.items.loading.caption" = "Chargement des produits"; -"paywall.items.full_version.extra_description" = "Tous les fournisseurs (incluant les prochains)\n%@"; -"paywall.items.restore.title" = "Restaurer les achats"; -"paywall.items.restore.description" = "Si vous avez acheté l'application ou une fonctionnalité dans le passé, vous pouvez restaurer les achats et ce message ne s'affichera plus."; -"paywall.alerts.purchase.appletv.success.message" = "Merci ! La limite de temps sera levée dès que iCloud se mettra à jour. Patientez quelques instants, puis relancez la connexion sur l'application TV."; - -/* MARK: DonateView */ - -"donate.title" = "Faire un don"; -"donate.sections.one_time.header" = "Une seule fois"; -"donate.sections.one_time.footer" = "Si vous voulez manifester votre gratitude envers mon travail bénévole, voici certains montants pour faire un don instantanément.\n\n Vous n'allez être chargé qu'une seule fois par don et vous pouvez faire un don plus d'une fois."; -"donate.items.loading.caption" = "Chargement des dons"; -"donate.items.purchasing.caption" = "Don en cours"; -"donate.alerts.purchase.success.message" = "Ceci signifie beaucoup pour moi et j'espère sincèrement que vous continuerez d'utiliser et de promouvoir cette app."; -"donate.alerts.purchase.failure.message" = "Impossible de faire le don. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Réglages"; -"settings.items.locks_in_background.caption" = "Verrouiller l'accès à l'application"; -"settings.items.donate.caption" = "Faire un don"; - -/* MARK: AboutView */ - -"about.title" = "À propos"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Crédits"; -"about.items.join_community.caption" = "Rejoindre la communauté"; -"about.items.write_review.caption" = "Écrire un avis"; -"about.items.share_twitter.caption" = "Tweetez!"; -"about.items.website.caption" = "Page d'accueil"; -"about.items.disclaimer.caption" = "Avis de non-responsabilité"; -"about.items.privacy_policy.caption" = "Politique de la vie privée"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Version"; -"version.labels.intro" = "Passepartout et TunnelKit sont codés et maintenu par Davide De Rosa (keeshux).\n\nLe code source de Passepartout et TunnelKit est publiquement disponible sur GitHub sous license GPLv3, vous pouvez trouver les liens sur la page d'accueil."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Crédits"; -"credits.sections.licenses.header" = "Licenses"; -"credits.sections.notices.header" = "Préavis"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Préférences"; -"preferences.sections.general.header" = "Général"; -"preferences.items.launches_on_login.caption" = "Lancer au démarrage"; -"preferences.items.launches_on_login.footer" = "Cochez pour lancer automatiquement l'application à la connexion ou au démarrage."; -"preferences.items.confirm_quit.caption" = "Notification de sortie"; -"preferences.items.confirm_quit.footer" = "Cochez pour recevoir une demande de confirmation lorsque vous quittez."; diff --git a/Passepartout/App/it.lproj/InfoPlist.strings b/Passepartout/App/it.lproj/InfoPlist.strings deleted file mode 100644 index 4a6c89bd..00000000 --- a/Passepartout/App/it.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Nome di accesso dell'attuale Wi-Fi"; - -"NSFaceIDUsageDescription" = "Sblocca l'app con Face ID"; diff --git a/Passepartout/App/it.lproj/Localizable.strings b/Passepartout/App/it.lproj/Localizable.strings deleted file mode 100644 index 339139b5..00000000 --- a/Passepartout/App/it.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Policy"; -"global.strings.networks" = "Reti"; -"global.strings.edit" = "Modifica"; -"global.strings.unknown" = "Sconosciuto"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Seleziona un endpoint"; - -"on_demand.sections.policy.footer.any" = "in qualsiasi rete"; -"on_demand.sections.policy.footer.including" = "solo nelle reti in basso"; -"on_demand.sections.policy.footer.excluding" = "eccetto nelle reti in basso"; -"on_demand.policy.any" = "Tutte le reti"; -"on_demand.policy.including" = "Includi"; -"on_demand.policy.excluding" = "Escludi"; - -"settings.sections.icloud.footer" = "Disabilita la sincronizzazione per permettere la cancellazione. Per cancellare in modo sicuro lo store iCloud, ripeti l'operazione su tutti i tuoi device sincronizzati. I profili locali non saranno modificati."; -"settings.items.should_enable_cloud_syncing.caption" = "Sincronizza con iCloud"; -"settings.items.erase_cloud_store.caption" = "Cancella store iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Annulla"; -"global.strings.next" = "Avanti"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Salva"; -"global.strings.rename" = "Rinomina"; -"global.strings.duplicate" = "Duplica"; -"global.strings.add" = "Aggiungi"; -"global.strings.delete" = "Cancella"; -"global.strings.uninstall" = "Disinstalla"; -"global.strings.default" = "Default"; -"global.strings.name" = "Nome"; -"global.strings.profiles" = "Profili"; -"global.strings.provider" = "Provider"; -"global.strings.providers" = "Provider"; -"global.strings.configuration" = "Configurazione"; -"global.strings.address" = "Indirizzo"; -"global.strings.addresses" = "Indirizzi"; -"global.strings.port" = "Porta"; -"global.strings.protocol" = "Protocollo"; -"global.strings.protocols" = "Protocolli"; -"global.strings.enabled" = "Abilitato"; -"global.strings.disabled" = "Disabilitato"; -"global.strings.none" = "Nessuno"; -"global.strings.automatic" = "Automatico"; -"global.strings.manual" = "Manuale"; -"global.strings.encryption" = "Crittografia"; -"global.strings.reconnect" = "Riconnetti"; -"global.strings.servers" = "Server"; -"global.strings.domain" = "Dominio"; -"global.strings.domains" = "Dominii"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interfaccia"; -"global.strings.private_key" = "Chiave privata"; -"global.strings.public_key" = "Chiave pubblica"; -"global.strings.endpoint" = "Endpoint"; -"global.strings.keepalive" = "Keep-alive"; -"global.strings.advanced" = "Avanzate"; -"global.strings.translations" = "Traduzioni"; -"global.strings.show" = "Mostra"; -"global.strings.connect" = "Connetti"; -"global.strings.disconnect" = "Disconnetti"; -"global.strings.download" = "Download"; -"global.strings.authentication" = "Autenticazione"; -"global.messages.unlock_app" = "Passepartout è bloccata"; -"global.messages.email_not_configured" = "Nessun account e-mail configurato."; -"global.messages.share" = "Passepartout è un client OpenVPN / WireGuard user-friendly ed open source per iOS e macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Ricordami più tardi"; -"global.alerts.buttons.never" = "Non chiedere più"; - -"global.placeholders.profile_name" = "Il mio profilo"; - -"global.errors.missing_profile" = "Profilo mancante"; -"global.errors.missing_account" = "Credenziali mancanti"; -"global.errors.missing_provider_server" = "Regione mancante"; -"global.errors.missing_provider_preset" = "Preset mancante"; -"global.errors.tunnel_expired" = "Connessione scaduta"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Supporto"; -"menu.all.share.title" = "Condividi"; -"menu.all.about.title" = "Informazioni su %@"; - -"menu.contextual.add_profile.from_files" = "Da Files"; -"menu.contextual.add_profile.from_text" = "Da testo"; -"menu.contextual.add_profile.imported" = "Aggiungi %@"; -"menu.contextual.support.write_review" = "Recensisci"; -"menu.contextual.support.join_community" = "Community"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Invita"; - -"menu.system.quit.title" = "Esci da %@"; - -"menu.system.quit.messages.confirm" = "La VPN, se abilitata, continuerà ad essere attiva in background. Vuoi comunque uscire?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Connettendo"; -"tunnelkit.vpn.active" = "Attiva"; -"tunnelkit.vpn.disconnecting" = "Disconnettendo"; -"tunnelkit.vpn.inactive" = "Inattiva"; -"tunnelkit.vpn.disabled" = "Disabilitata"; -"tunnelkit.vpn.unused" = "Spento"; - -"tunnelkit.errors.vpn.timeout" = "Timeout"; -"tunnelkit.errors.vpn.dns" = "DNS fallito"; -"tunnelkit.errors.vpn.auth" = "Autenticazione fallita"; -"tunnelkit.errors.vpn.tls" = "TLS fallito"; -"tunnelkit.errors.vpn.encryption" = "Crittografia fallita"; -"tunnelkit.errors.vpn.compression" = "Compressione non supportata"; -"tunnelkit.errors.vpn.network" = "Rete cambiata"; -"tunnelkit.errors.vpn.routing" = "Routing mancante"; -"tunnelkit.errors.vpn.gateway" = "Nessun gateway"; -"tunnelkit.errors.vpn.shutdown" = "Server arrestato"; - -"tunnelkit.errors.parsing" = "Impossibile processare il file di configurazione specificato (%@)."; -"tunnelkit.errors.openvpn.malformed" = "La configurazione contiene un'opzione malformata (%@)."; -"tunnelkit.errors.openvpn.required_option" = "La configurazione non contiene un'opzione obbligatoria (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "La configurazione contiene un'opzione non supportata (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "La configurazione è corretto ma contiene un'opzione potenzialmente non supportata (%@).\n\nLa connettività potrebbe fallire a seconda delle impostazioni del server."; -"tunnelkit.errors.openvpn.passphrase_required" = "Per favore inserisci la passphrase di criptazione."; -"tunnelkit.errors.openvpn.decryption" = "La configurazione contiene una chiave privata criptata e non è stato possibile decriptarla. Controlla la tua passphrase."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "In uso"; -"organizer.sections.tv.profiles_list.header.p1" = "Apri Passepartout sul tuo dispositivo iOS o macOS ed abilita lo switch \"Apple TV\" di un profilo per mostrarlo qui in basso."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Nessun profilo"; - -"organizer.alerts.reddit.message" = "Sapevi che Passepartout ha un subreddit? Iscriviti per aggiornamenti o per discutere problemi, aggiunte, nuove piattaforme o qualunque cosa tu voglia.\n\nÈ anche un ottimo modo per dimostrare che hai a cuore questo progetto."; -"organizer.alerts.reddit.buttons.subscribe" = "Iscriviti ora!"; - -"organizer.alerts.remove_profile.message" = "Sei sicuro di voler cancellare il profilo %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Nuovo profilo"; -"add_profile.shared.views.existing.header" = "Profili esistenti"; -"add_profile.shared.alerts.overwrite.message" = "Esiste già un profilo con lo stesso nome. Sostituire?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Inserisci passphrase"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Qui trovi alcuni provider con configurazioni precompilate."; -"add_profile.provider.items.update_list" = "Aggiorna lista"; -"add_profile.provider.errors.no_default_server" = "Nessun server disponibile."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Benvenuto in Passepartout!\n\nUsa il menu per aggiungere un nuovo profilo."; - -"profile.sections.vpn.footer" = "La connessione sarà stabilita ogni volta che è necessario."; -"profile.sections.status.header" = "Connessione"; -"profile.sections.provider_infrastructure.footer" = "Ultimo aggiornamento: %@."; -"profile.sections.tv.footer.encryption" = "I profili sono criptati e resi disponibili sulla tua Apple TV attraverso iCloud."; -"profile.sections.tv.footer.restricted.p1" = "Tuttavia, la connessione scadrà fra %d minuti."; -"profile.sections.tv.footer.restricted.p2" = "Acquista per rimuovere la restrizione."; -"profile.sections.vpn_survives_sleep.footer" = "Disabilita per migliorare il consumo della batteria, a discapito di rallentamenti occasionali causati dalle riconnessioni."; -"profile.sections.vpn_resolves_hostname.footer" = "Preferibile nella maggior parte delle reti e necessario in alcune reti IPv6. Disabilita dove il DNS è bloccato, o per velocizzare la negoziazione quando il DNS tarda a rispondere."; -"profile.sections.feedback.header" = "Feedback"; -"profile.items.use_profile.caption" = "Usa questo profilo"; -"profile.items.vpn_service.caption" = "Abilitato"; -"profile.items.vpn.turn_on.caption" = "Abilita VPN"; -"profile.items.vpn.turn_off.caption" = "Disabilita VPN"; -"profile.items.connection_status.caption" = "Stato"; -"profile.items.data_count.caption" = "Dati scambiati"; -"profile.items.randomizes_server.caption" = "Server casuale"; -"profile.items.provider.refresh.caption" = "Aggiorna infrastruttura"; -"profile.items.category.caption" = "Categoria"; -"profile.items.only_shows_favorites.caption" = "Mostra solo le posizioni preferite"; -"profile.items.vpn_survives_sleep.caption" = "Mantieni attivo in sleep"; -"profile.items.vpn_resolves_hostname.caption" = "Risolvi hostname del server"; -"profile.items.tv_sharing.caption.limited" = "Limitato a %d minuti"; -"profile.items.expires_at.caption" = "Scadenza"; - -"profile.alerts.rename.title" = "Rinomina profilo"; -"profile.alerts.reconnect_vpn.message" = "Vuoi riconnetterti alla VPN?"; -"profile.alerts.uninstall_vpn.message" = "Vuoi veramente cancellare la configurazione VPN dalle impostazioni del tuo dispositivo? Quest'azione potrebbe risolvere alcuni stati erronei della VPN e non altererà i tuoi provider e i tuoi host."; -"profile.alerts.test_connectivity.title" = "Connettività"; -"profile.alerts.test_connectivity.messages.success" = "Il tuo dispositivo è connesso a Internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Il tuo dispositivo non è connesso a Internet, per favore controlla i parametri del tuo profilo."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Regione"; -"provider.location.sections.empty_favorites.footer" = "Scorri a sinistra su una regione per aggiungerla o rimuoverla dai Preferiti."; -"provider.location.actions.favorite" = "Preferita"; -"provider.location.actions.unfavorite" = "Non preferita"; - -"provider.preset.title" = "Preset"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Account"; -"account.sections.credentials.header" = "Credenziali"; -"account.sections.registration.footer" = "Registra un account sul sito di %@."; -"account.items.authentication_method.persistent" = "Persistente"; -"account.items.authentication_method.interactive" = "Interattivo"; -"account.items.username.caption" = "Username"; -"account.items.username.placeholder" = "username"; -"account.items.password.caption" = "Password"; -"account.items.password.placeholder" = "segreto"; -"account.items.seed.caption" = "Seed"; -"account.items.open_guide.caption" = "Vedi le tue credenziali"; -"account.items.signup.caption" = "Registrati con %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Usa le credenziali del sito di %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Usa le tue credenziali del servizio %@, che potrebbero differire dalle credenziali del sito web."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Usa le credenziali del sito di %@. Il tuo username è generalmente numerico (senza spazi)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Usa le credenziali del sito di %@. Il tuo username è generalmente la tua e-mail."; -"account.sections.guidance.footer.infrastructure.pia" = "Usa le credenziali del sito di %@. Il tuo username è generalmente numerico con un prefisso \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Trova le tue credenziali nella sezione \"Account > OpenVPN / IKEv2 Username\" del sito di %@."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Usa le credenziali del sito di %@. Il tuo username è generalmente la tua e-mail."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Usa le credenziali del sito di %@. Il tuo username è generalmente la tua e-mail."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Trova le tue credenziali nell'OpenVPN Config Generator sul sito di %@."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Chiave condivisa"; -"endpoint.wireguard.items.allowed_ip.caption" = "IP ammesso"; - -"endpoint.advanced.title" = "Dettagli tecnici"; -"endpoint.advanced.openvpn.sections.pull.header" = "Ottieni dal server"; -"endpoint.advanced.openvpn.sections.communication.header" = "Comunicazione"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Se ti trovi con una connettività compromessa dopo aver cambiato i parametri di comunicazione, tocca per tornare alla configurazione originale."; -"endpoint.advanced.openvpn.sections.compression.header" = "Compressione"; -"endpoint.advanced.openvpn.sections.network.header" = "Rete"; -"endpoint.advanced.openvpn.sections.other.header" = "Altro"; -"endpoint.advanced.openvpn.items.route.caption" = "Rotta"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cifratura"; -"endpoint.advanced.openvpn.items.digest.caption" = "Autenticazione"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Incorporata"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algoritmo"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Non supportato"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Ripristina configurazione"; -"endpoint.advanced.openvpn.items.client.caption" = "Certificato"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Chiave"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Verificato"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Non verificato"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Autenticazione"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Criptazione"; -"endpoint.advanced.openvpn.items.eku.caption" = "Verifica estesa"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d secondi"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Rinegoziazione"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "dopo %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Endpoint casuale"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Hostname casuale"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Impostazioni di rete"; -"network_settings.sections.choices.header" = "Personalizza"; -"network_settings.gateway.title" = "Gateway predefinito"; -"network_settings.proxy.items.bypass_domains.caption" = "Dominii ignorati"; -"network_settings.items.add_dns_server.caption" = "Aggiungi indirizzo"; -"network_settings.items.add_dns_domain.caption" = "Aggiungi dominio"; -"network_settings.items.proxy_bypass.caption" = "Dominio ignorato"; -"network_settings.items.add_proxy_bypass.caption" = "Aggiungi dominio ignorato"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "On demand"; -"on_demand.sections.policy.footer" = "Attiva la VPN %@."; -"on_demand.items.add_ssid.caption" = "Aggiungi Wi-Fi"; -"on_demand.items.active.caption" = "Sicura"; -"on_demand.items.mobile.caption" = "Rete cellulare"; -"on_demand.items.ethernet.caption" = "Connessioni cablate"; - -"on_demand.items.policy.caption" = "Spegni VPN in rete sicura"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnostica"; -"diagnostics.sections.debug_log.footer" = "Il mascheramento sarà effettivo dopo una riconnessione. I dati di rete sono hostname, indirizzi IP, routing, SSID. Credenziali e chiavi private non sono registrati in ogni caso."; -"diagnostics.items.server_configuration.caption" = "Configurazione del server"; -"diagnostics.items.app_log.title" = "App"; -"diagnostics.items.masks_private_data.caption" = "Maschera dati rete"; -"diagnostics.items.report_issue.caption" = "Segnala problema connettività"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Per azzerare il debug log ed applicare la nuova preferenza di mascheramento, devi riconnetterti alla VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Debug log"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Copia"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Segnala problema"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Aggiungi comando rapido"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Cellulare"; -"shortcuts.add.items.connect.caption" = "Connetti a"; -"shortcuts.add.items.enable_vpn.caption" = "Abilita VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Disabilita VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Aggiungi Wi-Fi sicura"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Rimuovi Wi-Fi sicura"; -"shortcuts.add.items.trust_cellular.caption" = "Aggiungi rete mobile sicura"; -"shortcuts.add.items.untrust_cellular.caption" = "Rimuovi rete mobile sicura"; -"shortcuts.add.alerts.no_profiles.message" = "Non c'è nessun profilo a cui connettersi."; - -"shortcuts.edit.title" = "Gestisci comandi rapidi"; -"shortcuts.edit.sections.all.header" = "Comandi esistenti"; -"shortcuts.edit.sections.add.footer" = "Chiedi aiuto a Siri per velocizzare le tue interazioni più frequenti con l'app."; -"shortcuts.edit.items.add_shortcut.caption" = "Aggiungi comando rapido"; - -/* MARK: PaywallView */ - -"paywall.title" = "Acquista"; -"paywall.sections.products.footer" = "Ogni prodotto è un acquisto unico e non ricorrente. L'acquisto di un provider non include una sottoscrizione."; -"paywall.items.loading.caption" = "Caricando prodotti"; -"paywall.items.full_version.extra_description" = "Tutti i provider (inclusi quelli futuri)\n%@"; -"paywall.items.restore.title" = "Ripristina acquisti"; -"paywall.items.restore.description" = "Se hai comprato quest'applicazione o funzionalità in precedenza, puoi ripristinare i tuoi acquisti in modo che questa schermata non compaia più."; -"paywall.alerts.purchase.appletv.success.message" = "Grazie! Il limite di tempo sarà rimosso non appena iCloud si sarà sincronizzato. Aspetta un momento, dopodiché riavvia la connessione sull'app della TV."; - -/* MARK: DonateView */ - -"donate.title" = "Donazione"; -"donate.sections.one_time.header" = "Unica"; -"donate.sections.one_time.footer" = "Se vuoi mostrare gratitudine per il mio lavoro a titolo gratuito, qui trovi varie somme da donare all'istante.\n\nLa donazione ti sarà addebitata solo una volta, e puoi effettuare più donazioni."; -"donate.items.loading.caption" = "Caricando donazioni"; -"donate.items.purchasing.caption" = "Effettuando donazione"; -"donate.alerts.purchase.success.message" = "Questo significa molto per me e spero vivamente che tu continui ad usare e promuovere quest'applicazione."; -"donate.alerts.purchase.failure.message" = "Impossibile effettuare la donazione. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Impostazioni"; -"settings.items.locks_in_background.caption" = "Blocca l'accesso all'app"; -"settings.items.donate.caption" = "Fai una donazione"; - -/* MARK: AboutView */ - -"about.title" = "Informazioni su"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Credits"; -"about.items.join_community.caption" = "Entra nella community"; -"about.items.write_review.caption" = "Scrivi una recensione"; -"about.items.share_twitter.caption" = "Scrivi un Tweet!"; -"about.items.website.caption" = "Home page"; -"about.items.disclaimer.caption" = "Disclaimer"; -"about.items.privacy_policy.caption" = "Privacy policy"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Versione"; -"version.labels.intro" = "Passepartout e TunnelKit sono scritti e mantenuti da Davide De Rosa (keeshux).\n\nIl codice sorgente di Passepartout e TunnelKit è pubblicamente disponibile su GitHub in accordo con la GPLv3, puoi trovare i link nella home page."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Credits"; -"credits.sections.licenses.header" = "Licenze"; -"credits.sections.notices.header" = "Notice"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Preferenze"; -"preferences.sections.general.header" = "Generale"; -"preferences.items.launches_on_login.caption" = "Apri al login"; -"preferences.items.launches_on_login.footer" = "Seleziona per aprire automaticamente l'app all'avvio o al login."; -"preferences.items.confirm_quit.caption" = "Conferma uscita"; -"preferences.items.confirm_quit.footer" = "Seleziona per confermare l'uscita dall'applicazione."; diff --git a/Passepartout/App/nl.lproj/InfoPlist.strings b/Passepartout/App/nl.lproj/InfoPlist.strings deleted file mode 100644 index 7d18e7e5..00000000 --- a/Passepartout/App/nl.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Toegangsnaam van huidig Wi-Fi-netwerk"; - -"NSFaceIDUsageDescription" = "Ontgrendel app met gezichtsherkenning"; diff --git a/Passepartout/App/nl.lproj/Localizable.strings b/Passepartout/App/nl.lproj/Localizable.strings deleted file mode 100644 index 5d7567a0..00000000 --- a/Passepartout/App/nl.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Beleid"; -"global.strings.networks" = "Netwerken"; -"global.strings.edit" = "Bewerken"; -"global.strings.unknown" = "Onbekend"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Selecteer een eindpunt"; - -"on_demand.sections.policy.footer.any" = "in welk netwerk dan ook"; -"on_demand.sections.policy.footer.including" = "alleen in de netwerken hieronder"; -"on_demand.sections.policy.footer.excluding" = "niet in de netwerken hieronder"; -"on_demand.policy.any" = "Alle netwerken"; -"on_demand.policy.including" = "Bevat wel"; -"on_demand.policy.excluding" = "Bevat niet"; - -"settings.sections.icloud.footer" = "Synchroniseren uitschakelen om wissen toe te staan. Om de iCloud-opslag veilig te wissen, moet u dit doen op alle gesynchroniseerde apparaten. Dit heeft geen invloed op lokale profielen."; -"settings.items.should_enable_cloud_syncing.caption" = "Met iCloud synchroniseren"; -"settings.items.erase_cloud_store.caption" = "iCloud-opslag wissen"; - -/* MARK: Global */ - -"global.strings.cancel" = "Afbreken"; -"global.strings.next" = "Volgende"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Opslaan"; -"global.strings.rename" = "Hernoemen"; -"global.strings.duplicate" = "Dupliceren"; -"global.strings.add" = "Toevoegen"; -"global.strings.delete" = "Verwijderen"; -"global.strings.uninstall" = "Installatie ongedaan maken"; -"global.strings.default" = "Default"; -"global.strings.name" = "Naam"; -"global.strings.profiles" = "Profielen"; -"global.strings.provider" = "Aanbieder"; -"global.strings.providers" = "Aanbieders"; -"global.strings.configuration" = "Configuratie"; -"global.strings.address" = "Adress"; -"global.strings.addresses" = "Adressen"; -"global.strings.port" = "Port"; -"global.strings.protocol" = "Protocol"; -"global.strings.protocols" = "Protocollen"; -"global.strings.enabled" = "Ingeschakeld"; -"global.strings.disabled" = "Uitgeschakeld"; -"global.strings.none" = "Geen"; -"global.strings.automatic" = "Automatisch"; -"global.strings.manual" = "Handmatig"; -"global.strings.encryption" = "Versleuteling"; -"global.strings.reconnect" = "Opnieuw verbinden"; -"global.strings.servers" = "Servers"; -"global.strings.domain" = "Domein"; -"global.strings.domains" = "Domeinen"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interface"; -"global.strings.private_key" = "Persoonlijke sleutel"; -"global.strings.public_key" = "Openbare sleutel"; -"global.strings.endpoint" = "Eindpunt"; -"global.strings.keepalive" = "Keep-alive"; -"global.strings.advanced" = "Geavanceerd"; -"global.strings.translations" = "Vertalingen"; -"global.strings.show" = "Weergeven"; -"global.strings.connect" = "Verbinding maken"; -"global.strings.disconnect" = "Verbinding verbreken"; -"global.strings.download" = "Downloaden"; -"global.strings.authentication" = "Authenticatie"; -"global.messages.unlock_app" = "Hoofdsleutel is vergrendeld"; -"global.messages.email_not_configured" = "Er is geen email adres geconfigureerd."; -"global.messages.share" = "Passepartout is een gebruiksvriendelijke open source OpenVPN / WireGuard client voor iOS en macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Herinner me later"; -"global.alerts.buttons.never" = "Vraag dit niet meer"; - -"global.placeholders.profile_name" = "Mijn Profiel"; - -"global.errors.missing_profile" = "Ontbrekend profiel"; -"global.errors.missing_account" = "Ontbrekend account"; -"global.errors.missing_provider_server" = "Ontbrekende locatie"; -"global.errors.missing_provider_preset" = "Ontbrekende voorkeur"; -"global.errors.tunnel_expired" = "Verbinding verlopen"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Ondersteuning"; -"menu.all.share.title" = "Delen"; -"menu.all.about.title" = "Over %@"; - -"menu.contextual.add_profile.from_files" = "Vanuit bestanden"; -"menu.contextual.add_profile.from_text" = "Vanuit tekst"; -"menu.contextual.add_profile.imported" = "%@ toevoegen"; -"menu.contextual.support.write_review" = "Beoordelen"; -"menu.contextual.support.join_community" = "Gemeenschap"; -"menu.contextual.share_twitter" = "Tweeten"; -"menu.contextual.share_generic" = "Uitnodigen"; - -"menu.system.quit.title" = "%@ afsluiten"; - -"menu.system.quit.messages.confirm" = "De VPN zal, indien geactiveerd, op de achtergrond blijven draaien. Wilt u sluiten?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Verbinden"; -"tunnelkit.vpn.active" = "Actief"; -"tunnelkit.vpn.disconnecting" = "Verbinding verbreken"; -"tunnelkit.vpn.inactive" = "Inactief"; -"tunnelkit.vpn.disabled" = "Uitgeschakeld"; -"tunnelkit.vpn.unused" = "Uit"; - -"tunnelkit.errors.vpn.timeout" = "Time-out"; -"tunnelkit.errors.vpn.dns" = "DNS niet gelukt"; -"tunnelkit.errors.vpn.auth" = "Auth niet gelukt"; -"tunnelkit.errors.vpn.tls" = "TLS niet gelukt"; -"tunnelkit.errors.vpn.encryption" = "Versleuteling mislukt"; -"tunnelkit.errors.vpn.compression" = "Compressie wordt niet ondersteund"; -"tunnelkit.errors.vpn.network" = "Netwerk veranderd"; -"tunnelkit.errors.vpn.routing" = "Ontbrekende routering"; -"tunnelkit.errors.vpn.gateway" = "Geen gateway"; -"tunnelkit.errors.vpn.shutdown" = "Server is afgesloten"; - -"tunnelkit.errors.parsing" = "Kan het opgegeven configuratiebestand niet verwerken (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Het configuratie betand bevat ongeldige optie(s) (%@)."; -"tunnelkit.errors.openvpn.required_option" = "Het configuratiebestand mist een vereiste optie (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Het configuratiebestand bevat een niet-ondersteunde optie (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Het configuratiebestand is correct maar bevat mogelijk een niet-ondersteunde optie (%@).\n\nConnectiviteit kan hier door niet werken, afhankelijk van de serverinstellingen."; -"tunnelkit.errors.openvpn.passphrase_required" = "Voer een coderingswachtwoord in"; -"tunnelkit.errors.openvpn.decryption" = "De configuratie bevat een gecodeerde privésleutel en deze kan niet worden gedecodeerd. Controleer de ingevoerde wachtwoordzin nogmaals."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "In gebruik"; -"organizer.sections.tv.profiles_list.header.p1" = "Open Passepartout op je iOS- of macOS-apparaat en activeer de 'Apple TV'-schakelaar van een profiel om dit hier weer te geven."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Geen profielen"; - -"organizer.alerts.reddit.message" = "Wist je dat Passepartout een eigen subreddit heeft? Schrijf je in voor updates, of discussiëren over problemen, (nieuwe) mogelijkheden, nieuwe platformen of wat je maar wil.\n\nHet is ook een goede manier om te laten zien dat je om dit project geeft."; -"organizer.alerts.reddit.buttons.subscribe" = "Schfijf je nu in!"; - -"organizer.alerts.remove_profile.message" = "Weet u zeker dat u profiel %@ wilt verwijderen?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Nieuw profiel"; -"add_profile.shared.views.existing.header" = "Bestaande profielen"; -"add_profile.shared.alerts.overwrite.message" = "Er bestaat al een profiel met deze naam, wil je hem vervangen?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Voer wachtwoordzin in"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Hier vind je aan aantal aanbieders met configuratie profielen."; -"add_profile.provider.items.update_list" = "Lijst bijwerken"; -"add_profile.provider.errors.no_default_server" = "Geen server gevonden."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Welkom bij Passepartout!\n\nGebruik de organizer om een nieuw profiel toe te voegen."; - -"profile.sections.vpn.footer" = "De verbinding zal worden gestart wanneer nodig."; -"profile.sections.status.header" = "Verbinding"; -"profile.sections.provider_infrastructure.footer" = "Laatste update was op %@."; -"profile.sections.tv.footer.encryption" = "Profielen zijn gecodeerd en via iCloud beschikbaar voor je Apple TV."; -"profile.sections.tv.footer.restricted.p1" = "Maar de verbinding verloopt na %d minuten."; -"profile.sections.tv.footer.restricted.p2" = "Doe een aankoop om de beperking op te heffen."; -"profile.sections.vpn_survives_sleep.footer" = "Uitschakelen om het batterijverbruik te verbeteren, ten koste van incidentele vertragingen als gevolg van het opnieuw opstarten na wake-up."; -"profile.sections.vpn_resolves_hostname.footer" = "Voorkeur om dit aan te zetten voor de meeste netwerken en vereist in sommige IPv6-netwerken. Uitschakelen waar DNS wordt geblokkeerd, of om de onderhandelingen te versnellen wanneer DNS traag reageert."; -"profile.sections.feedback.header" = "Terugkoppeling"; -"profile.items.use_profile.caption" = "Gebruik dit profiel"; -"profile.items.vpn_service.caption" = "Ingeschakeld"; -"profile.items.vpn.turn_on.caption" = "VPN activeren"; -"profile.items.vpn.turn_off.caption" = "VPN deactiveren"; -"profile.items.connection_status.caption" = "Status"; -"profile.items.data_count.caption" = "Gegegevens uitgewisseld"; -"profile.items.randomizes_server.caption" = "Willekeurige server"; -"profile.items.provider.refresh.caption" = "Vernieuw de infrastructuur"; -"profile.items.category.caption" = "Categorie"; -"profile.items.only_shows_favorites.caption" = "Alleen favoriete locaties weergeven"; -"profile.items.vpn_survives_sleep.caption" = "Actief tijdens slaapstand"; -"profile.items.vpn_resolves_hostname.caption" = "Haal de naam van de host op"; -"profile.items.tv_sharing.caption.limited" = "Beperkt tot %d minuten"; -"profile.items.expires_at.caption" = "Verlooptijd"; - -"profile.alerts.rename.title" = "Profiel hernoemen"; -"profile.alerts.reconnect_vpn.message" = "Opnieuw verbinding maken met de VPN?"; -"profile.alerts.uninstall_vpn.message" = "Wilt u de VPN-configuratie van uw apparaatinstellingen verwijderen? Dit kan enkele problemen met VPN oplossen en heeft geen invloed op uw provider- en hostprofielen."; -"profile.alerts.test_connectivity.title" = "Connectiviteit"; -"profile.alerts.test_connectivity.messages.success" = "Apparaat is verbonden met internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Uw apparaat heeft geen internetverbinding. Controleer uw profielparameters."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Locatie"; -"provider.location.sections.empty_favorites.footer" = "Veeg naar links op een locatie om deze toe te voegen of te verwijderen aan Favorieten."; -"provider.location.actions.favorite" = "Favoriet"; -"provider.location.actions.unfavorite" = "Geen favoriet"; - -"provider.preset.title" = "Voorkeur"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Account"; -"account.sections.credentials.header" = "Inloggegevens"; -"account.sections.registration.footer" = "Registreer voor een %@ account op de website."; -"account.items.authentication_method.persistent" = "Aanhoudend"; -"account.items.authentication_method.interactive" = "Interactief"; -"account.items.username.caption" = "Gebruikersnaam"; -"account.items.username.placeholder" = "gebruikersnaam"; -"account.items.password.caption" = "Wachtwoord"; -"account.items.password.placeholder" = "geheim"; -"account.items.seed.caption" = "Seed"; -"account.items.open_guide.caption" = "Bekijk de inloggegevens"; -"account.items.signup.caption" = "Registreer bij %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Gebruik de inloggegevens van %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Gebruik uw %@ service-gegevens, die kunnen verschillen van de gegevens van de website."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal numeriek (zonder ruimte)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal uw e-mailadres."; -"account.sections.guidance.footer.infrastructure.pia" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal numeriek met \"p\" als voorvoegsel."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Vind de inloggegevens van %@ in \"Account > OpenVPN / IKEv2 Username\" onderdeel van de website."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Gebruik de inloggegevens van %@. Uw gebruikersnaam is meestal uw e-mailadres."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Gebruik de inloggegevens van %@ Uw gebruikersnaam is meestal uw e-mailadres."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Vind de inloggegevens van %@ in de OpenVPN Config Generator op de website."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Vooraf gedeelde sleutel"; -"endpoint.wireguard.items.allowed_ip.caption" = "Toegestane IP"; - -"endpoint.advanced.title" = "Technische details"; -"endpoint.advanced.openvpn.sections.pull.header" = "Pull van server"; -"endpoint.advanced.openvpn.sections.communication.header" = "Communicatie"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Tik hier als de connectiviteit niet meer werkt na het aanpassen van instellingen, om terug te gaan naar de originele configuratie."; -"endpoint.advanced.openvpn.sections.compression.header" = "Compressie"; -"endpoint.advanced.openvpn.sections.network.header" = "Netwerk"; -"endpoint.advanced.openvpn.sections.other.header" = "Ander"; -"endpoint.advanced.openvpn.items.route.caption" = "Route"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cipher"; -"endpoint.advanced.openvpn.items.digest.caption" = "Authenticatie"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Embedded"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algoritme"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Niet ondersteund"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Reset configuratie"; -"endpoint.advanced.openvpn.items.client.caption" = "Certificaat"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Sleutel"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Geverifieerd"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Niet geverifieerd"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Authenticatie"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Versleuteling"; -"endpoint.advanced.openvpn.items.eku.caption" = "Uitgebreide verificatie"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d seconden"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Renegotiation"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "na %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Willekeurig eindpunt"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Hostnamen randomiseren"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Netwerk instellingen"; -"network_settings.sections.choices.header" = "Overschrijven"; -"network_settings.gateway.title" = "Standaard gateway"; -"network_settings.proxy.items.bypass_domains.caption" = "Domeinen omzeilen"; -"network_settings.items.add_dns_server.caption" = "Voeg adress toe"; -"network_settings.items.add_dns_domain.caption" = "Zoekdomein toevoegen"; -"network_settings.items.proxy_bypass.caption" = "Omzeil domein"; -"network_settings.items.add_proxy_bypass.caption" = "Voeg omzeil optie voor domein toe"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "On-demand"; -"on_demand.sections.policy.footer" = "Activeer de VPN %@."; -"on_demand.items.add_ssid.caption" = "Wi-Fi toevoegen"; -"on_demand.items.active.caption" = "Vertrouwen"; -"on_demand.items.mobile.caption" = "Mobiel netwerk"; -"on_demand.items.ethernet.caption" = "Bekabelde verbindingen"; - -"on_demand.items.policy.caption" = "Trust disables VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnose"; -"diagnostics.sections.debug_log.footer" = "De maskeerstatus is effectief na opnieuw verbinden. Netwerkgegevens zijn hostnamen, IP-adressen, routing, SSID's. Inloggegevens en privésleutels worden niet geregistreerd."; -"diagnostics.items.server_configuration.caption" = "Server configuratie"; -"diagnostics.items.app_log.title" = "App"; -"diagnostics.items.masks_private_data.caption" = "Netwerkgegevens maskeren"; -"diagnostics.items.report_issue.caption" = "Probleem met connectiviteit melden"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Om het huidige foutopsporingslogboek veilig opnieuw in te stellen en de nieuwe maskeervoorkeur toe te passen, moet u nu opnieuw verbinding maken met VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Foutopsporingslogboek"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Kopiëren"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Meld een probleem"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Voeg snelkoppeling toe"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Mobiel"; -"shortcuts.add.items.connect.caption" = "Verbind met"; -"shortcuts.add.items.enable_vpn.caption" = "Activeer VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Deactiveer VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Vertrouw huidig Wi-Fi netwerk"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Untrust current Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Vertouw mobiel netwerk"; -"shortcuts.add.items.untrust_cellular.caption" = "Untrust mobiel netwerk"; -"shortcuts.add.alerts.no_profiles.message" = "Er is geen profiel om verbinding mee te maken."; - -"shortcuts.edit.title" = "Beheer snelkoppelingen"; -"shortcuts.edit.sections.all.header" = "Bestaande snelkoppelingen"; -"shortcuts.edit.sections.add.footer" = "Krijg hulp van Siri en versnel de meest gebruikte interacties binnen de app."; -"shortcuts.edit.items.add_shortcut.caption" = "Voeg snelkoppeling toe"; - -/* MARK: PaywallView */ - -"paywall.title" = "Aanschaffen"; -"paywall.sections.products.footer" = "Elk product is een eenmalige aankoop. Aankopen van providers bevatten geen VPN-abonnement."; -"paywall.items.loading.caption" = "Producten laden"; -"paywall.items.full_version.extra_description" = "Alle providers (inclusief toekomstige)\n%@"; -"paywall.items.restore.title" = "Herstel Aankopen"; -"paywall.items.restore.description" = "Als u deze app of functie in het verleden heeft gekocht, kunt u uw aankopen herstellen en wordt dit scherm niet meer getoond."; -"paywall.alerts.purchase.appletv.success.message" = "Bedankt! De tijdsbeperking wordt opgeheven zodra iCloud dit ziet. Wacht even en herstart dan de verbinding op de tv-app."; - -/* MARK: DonateView */ - -"donate.title" = "Donatie"; -"donate.sections.one_time.header" = "Eenmalig"; -"donate.sections.one_time.footer" = "Als je dankbaarheid wilt tonen voor mijn gratis werk, zijn hier een paar bedragen die je direct kunt doneren.\n\nHet bedrag wordt slechts één keer per donatie in rekening gebracht en u kunt meerdere keren doneren."; -"donate.items.loading.caption" = "Ophalen donaties"; -"donate.items.purchasing.caption" = "Doneren"; -"donate.alerts.purchase.success.message" = "Dit betekent veel voor mij en ik hoop echt dat je deze app blijft gebruiken en promoten."; -"donate.alerts.purchase.failure.message" = "Donatie mislukt. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Instellingen"; -"settings.items.locks_in_background.caption" = "Toegang app vergrendelen"; -"settings.items.donate.caption" = "Doneer een gift"; - -/* MARK: AboutView */ - -"about.title" = "Over"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Credits"; -"about.items.join_community.caption" = "Word lid van de gemeenschap"; -"about.items.write_review.caption" = "Schrijf een beoordeling"; -"about.items.share_twitter.caption" = "Tweet erover!"; -"about.items.website.caption" = "Home page"; -"about.items.disclaimer.caption" = "Vrijwaring"; -"about.items.privacy_policy.caption" = "Privacybeleid"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Versie"; -"version.labels.intro" = "Passepartout en TunnelKit zijn geschreven en worden onderhouden door Davide De Rosa (keeshux).\n\nDe broncode voor Passepartout en TunnelKit is openbaar beschikbaar op GitHub onder de GPLv3, je kunt links op de startpagina vinden."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Credits"; -"credits.sections.licenses.header" = "Licenties"; -"credits.sections.notices.header" = "Mededelingen"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Voorkeuren"; -"preferences.sections.general.header" = "Algemeen"; -"preferences.items.launches_on_login.caption" = "Lanceren bij aanmelden"; -"preferences.items.launches_on_login.footer" = "Vink aan als u wilt dat de app automatisch wordt gelanceerd bij opstarten of aanmelden."; -"preferences.items.confirm_quit.caption" = "Sluiten bevestigen"; -"preferences.items.confirm_quit.footer" = "Vink aan om een bevestigingsmelding te sluiten."; diff --git a/Passepartout/App/pl.lproj/InfoPlist.strings b/Passepartout/App/pl.lproj/InfoPlist.strings deleted file mode 100644 index 3e058b4a..00000000 --- a/Passepartout/App/pl.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Nazwa dostępu do bieżącej sieci Wi-Fi"; - -"NSFaceIDUsageDescription" = "Odblokuj aplikację za pomocą Face ID"; diff --git a/Passepartout/App/pl.lproj/Localizable.strings b/Passepartout/App/pl.lproj/Localizable.strings deleted file mode 100644 index 7ca3b968..00000000 --- a/Passepartout/App/pl.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Polityka"; -"global.strings.networks" = "Sieci"; -"global.strings.edit" = "Edytuj"; -"global.strings.unknown" = "Nieznane"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Wybierz punkt końcowy"; - -"on_demand.sections.policy.footer.any" = "w dowolnej sieci"; -"on_demand.sections.policy.footer.including" = "tylko w poniższych sieciach"; -"on_demand.sections.policy.footer.excluding" = "z wyjątkiem poniższych sieci"; -"on_demand.policy.any" = "Wszystkie sieci"; -"on_demand.policy.including" = "Uwzględnij"; -"on_demand.policy.excluding" = "Wyklucz"; - -"settings.sections.icloud.footer" = "Wyłącz synchronizację, aby umożliwić wymazywanie. Aby bezpiecznie wymazać magazyn iCloud, należy to zrobić na wszystkich zsynchronizowanych urządzeniach. Nie będzie to miało wpływu na profile lokalne."; -"settings.items.should_enable_cloud_syncing.caption" = "Synchronizacja z iCloud"; -"settings.items.erase_cloud_store.caption" = "Wymaż pamięć iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Anuluj"; -"global.strings.next" = "Następny"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Zapisz"; -"global.strings.rename" = "Zmień nazwę"; -"global.strings.duplicate" = "Duplikuj"; -"global.strings.add" = "Dodaj"; -"global.strings.delete" = "Usuń"; -"global.strings.uninstall" = "Odinstaluj"; -"global.strings.default" = "Default"; -"global.strings.name" = "Nazwa"; -"global.strings.profiles" = "Profile"; -"global.strings.provider" = "Dostawca"; -"global.strings.providers" = "Usługodawcy"; -"global.strings.configuration" = "Konfiguracja"; -"global.strings.address" = "Adres"; -"global.strings.addresses" = "Adresy"; -"global.strings.port" = "Port"; -"global.strings.protocol" = "Protokół"; -"global.strings.protocols" = "Protokoły"; -"global.strings.enabled" = "Włączony"; -"global.strings.disabled" = "Wyłączony"; -"global.strings.none" = "Brak"; -"global.strings.automatic" = "Automatycznie"; -"global.strings.manual" = "Ręcznie"; -"global.strings.encryption" = "Szyfrowanie"; -"global.strings.reconnect" = "Połącz ponownie"; -"global.strings.servers" = "Serwery"; -"global.strings.domain" = "Domena"; -"global.strings.domains" = "Domeny"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interfejs"; -"global.strings.private_key" = "Klucz prywatny"; -"global.strings.public_key" = "Klucz publiczny"; -"global.strings.endpoint" = "Punkt końcowy"; -"global.strings.keepalive" = "Utrzymuj połączenie"; -"global.strings.advanced" = "Zaawansowane"; -"global.strings.translations" = "Tłumaczenia"; -"global.strings.show" = "Pokaż"; -"global.strings.connect" = "Połącz"; -"global.strings.disconnect" = "Rozłącz"; -"global.strings.download" = "Pobierz"; -"global.strings.authentication" = "Uwierzytelnianie"; -"global.messages.unlock_app" = "Passepartout jest zablokowany"; -"global.messages.email_not_configured" = "Adres e-mail nie jest skonfigurowany."; -"global.messages.share" = "Passepartout to klient OpenVPN / WireGuard, przyjazny użytkownikowi, open-source, stworzony dla iOS i macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Przypomnij mi później"; -"global.alerts.buttons.never" = "Nie przypominaj"; - -"global.placeholders.profile_name" = "Mój profil"; - -"global.errors.missing_profile" = "Brakujący profil"; -"global.errors.missing_account" = "Brakujące konto"; -"global.errors.missing_provider_server" = "Brakująca lokalizacja"; -"global.errors.missing_provider_preset" = "Brakujący preset"; -"global.errors.tunnel_expired" = "Połączenie wygasło"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Wsparcie"; -"menu.all.share.title" = "Udostępnij"; -"menu.all.about.title" = "O %@"; - -"menu.contextual.add_profile.from_files" = "Z plików"; -"menu.contextual.add_profile.from_text" = "Z tekstu"; -"menu.contextual.add_profile.imported" = "Dodaj %@"; -"menu.contextual.support.write_review" = "Recenzja"; -"menu.contextual.support.join_community" = "Społeczność"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Zaproś"; - -"menu.system.quit.title" = "Zakończ %@"; - -"menu.system.quit.messages.confirm" = "Jeśli sieć VPN, jest włączona, będzie nadal działać w tle. Chcesz zakończyć?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Łączenie"; -"tunnelkit.vpn.active" = "Aktywne"; -"tunnelkit.vpn.disconnecting" = "Rozłączanie"; -"tunnelkit.vpn.inactive" = "Nieaktywne"; -"tunnelkit.vpn.disabled" = "Wyłączone"; -"tunnelkit.vpn.unused" = "Wył"; - -"tunnelkit.errors.vpn.timeout" = "Upłynąl limit czasu połączenia"; -"tunnelkit.errors.vpn.dns" = "Błąd DNS"; -"tunnelkit.errors.vpn.auth" = "Błąd autoryzacji"; -"tunnelkit.errors.vpn.tls" = "Błąd TLS"; -"tunnelkit.errors.vpn.encryption" = "Błąd szyfrowania"; -"tunnelkit.errors.vpn.compression" = "Niewspierana kompresja"; -"tunnelkit.errors.vpn.network" = "Sieć zmieniona"; -"tunnelkit.errors.vpn.routing" = "Brak routingu"; -"tunnelkit.errors.vpn.gateway" = "Brak bramy domyślnej"; -"tunnelkit.errors.vpn.shutdown" = "Serwer został zamknięty"; - -"tunnelkit.errors.parsing" = "Błąd w przetwarzaniu pliku konfiguracyjnego (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Plik konfiguracyjny zawiera źle zformułowaną opcję (%@)."; -"tunnelkit.errors.openvpn.required_option" = "Plik konfiguracyjny nie nawiera potrzebnej opcji (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Plik konfiguracyjny posiada niewspieraną opcję (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Plik konfiguracyjny jest poprawny, ale zawiera potencjalnie niewspieraną opcję (%@).\n\nPołączenie może zostać przerwane przez ustawienia serwera."; -"tunnelkit.errors.openvpn.passphrase_required" = "Proszę wpisać frazę szyfrującą."; -"tunnelkit.errors.openvpn.decryption" = "Konfiguracja zawiera zaszyfrowany klucz prywatny który nie może zostać odszyfrowany. Sprawdź frazę szyfrującą."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "W użyciu"; -"organizer.sections.tv.profiles_list.header.p1" = "Otwórz Passepartout na urządzeniu z systemem iOS lub macOS i włącz przełącznik \"Apple TV\" profilu, aby pojawił się tutaj."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Brak profili"; - -"organizer.alerts.reddit.message" = "Wiedziałeś/łaś, że Passepartout ma swój subreddit? Subskrybuj dla aktualizacji, dyskusji o funkcjonalności, nowych platformach lub o czymkolwiek zechcesz.\n\nTo również świetny sposób na okazanie zainteresowania projektem."; -"organizer.alerts.reddit.buttons.subscribe" = "Subskrybuj!"; - -"organizer.alerts.remove_profile.message" = "Na pewno chcesz usunąć profil %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Nowy profil"; -"add_profile.shared.views.existing.header" = "Istniejące profile"; -"add_profile.shared.alerts.overwrite.message" = "Profil hosta z taką nazwą już istnieje. Nadpisać profil?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Wpisz frazę szyfrującą"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Tutaj znajdziesz kilku usługodawców z profilami konfiguracyjnymi."; -"add_profile.provider.items.update_list" = "Zaktualizuj listę"; -"add_profile.provider.errors.no_default_server" = "Nie znaleziono żadnego serwera."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Witaj w Passepartout!\n\nUżyj organizera by utworzyć nowy profil."; - -"profile.sections.vpn.footer" = "Połączenie zostanie nawiązane zgodnie z ustawieniami."; -"profile.sections.status.header" = "Połączenie"; -"profile.sections.provider_infrastructure.footer" = "Ostatnio aktualizowane %@."; -"profile.sections.tv.footer.encryption" = "Profile są szyfrowane i udostępniane Apple TV za pośrednictwem iCloud."; -"profile.sections.tv.footer.restricted.p1" = "Połączenie wygaśnie jednak po %d minutach."; -"profile.sections.tv.footer.restricted.p2" = "Zakup w celu zniesienia ograniczenia."; -"profile.sections.vpn_survives_sleep.footer" = "Wyłącz dla mniejszego zużycia baterii kosztem wolniejszego działania spowodowanego ponownym połączeniem przy wybudzeniu urządzenia."; -"profile.sections.vpn_resolves_hostname.footer" = "Preferowane w większości sieci i potrzebne w niektórych sieciach IPv6. Wyłącz kiedy DNS jest zablokowane, lub żeby przyspieszyć ustanawianie połączenia gdy DNS jest zbyt wolne."; -"profile.sections.feedback.header" = "Wyraź opinię"; -"profile.items.use_profile.caption" = "Używaj tego profilu"; -"profile.items.vpn_service.caption" = "Włączone"; -"profile.items.vpn.turn_on.caption" = "Włącz VPN"; -"profile.items.vpn.turn_off.caption" = "Wyłącz VPN"; -"profile.items.connection_status.caption" = "Status"; -"profile.items.data_count.caption" = "Pobrane/wysłane dane"; -"profile.items.randomizes_server.caption" = "Randomizuj serwer"; -"profile.items.provider.refresh.caption" = "Odśwież infrastrukturę"; -"profile.items.category.caption" = "Kategoria"; -"profile.items.only_shows_favorites.caption" = "Pokazuj tylko ulubione lokalizacje"; -"profile.items.vpn_survives_sleep.caption" = "Utrzymuj połączenie przy zablokowanym ekranie"; -"profile.items.vpn_resolves_hostname.caption" = "Rozwiązuj nazwy hostów usługodawcy"; -"profile.items.tv_sharing.caption.limited" = "Ograniczenie do %d minut"; -"profile.items.expires_at.caption" = "Wygaśnięcie"; - -"profile.alerts.rename.title" = "Zmień nazwę profilu"; -"profile.alerts.reconnect_vpn.message" = "Czy chcesz połączyć się ponownie z VPN?"; -"profile.alerts.uninstall_vpn.message" = "Na pewno chcesz usunąć konfigurację VPN z urządzenia? Może to naprawić błędy z statusem VPN i nie będzie miało wpływu na konfigurację usługodawców/hostów."; -"profile.alerts.test_connectivity.title" = "Połączenie"; -"profile.alerts.test_connectivity.messages.success" = "Twoje urządzenie jest połączone z internetem!"; -"profile.alerts.test_connectivity.messages.failure" = "Twoje urządzenie nie jest połączone z internetem, sprawdź ustawienia profilu."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Lokalizacja"; -"provider.location.sections.empty_favorites.footer" = "Aby usunąć zakładkę, przesuń w lewo."; -"provider.location.actions.favorite" = "Dodaj do ulubionych"; -"provider.location.actions.unfavorite" = "Usuń z ulubionych"; - -"provider.preset.title" = "Preset"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Konto"; -"account.sections.credentials.header" = "Dane logowania"; -"account.sections.registration.footer" = "Utwórz konto na stronie: %@"; -"account.items.authentication_method.persistent" = "Nieustający"; -"account.items.authentication_method.interactive" = "Interaktywny"; -"account.items.username.caption" = "Nazwa użytkownika"; -"account.items.username.placeholder" = "Nazwa użytkownika"; -"account.items.password.caption" = "Hasło"; -"account.items.password.placeholder" = "Ukryte"; -"account.items.seed.caption" = "Ziarno"; -"account.items.open_guide.caption" = "Zobacz swoje login/hasło"; -"account.items.signup.caption" = "Zarejstruj się w %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Użyj loginu do %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Użyj poświadczeń usługi %@, które mogą różnić się od poświadczeń witryny."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Użyj loginu do %@. Twoja nazwa użytkownika jest najczęściej ciągiem liczb (bez przestrzeni)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Użyj loginu do %@. Twoja nazwa użytkownika to najczęściej e-mail."; -"account.sections.guidance.footer.infrastructure.pia" = "Użyj loginu do %@. Twoja nazwa użytkownika jesy najczęściej ciągiem liczb poprzedonym prefiksem \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Użyj loginu do %@. Zajdziesz go w sekcji \"Account > OpenVPN / IKEv2 Username\"."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Użyj loginu do %@. Twoja nazwa użytkownika to najczęściej e-mail."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Użyj loginu do %@. Twoja nazwa użytkownika to najczęściej e-mail."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Użyj loginu do %@ z generatora konfiguracji OpenVPN dostępnego na stronie."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Wcześniej udostępniony klucz"; -"endpoint.wireguard.items.allowed_ip.caption" = "IP z zezwoleniem"; - -"endpoint.advanced.title" = "Dane techniczne"; -"endpoint.advanced.openvpn.sections.pull.header" = "Pobierz z serwera"; -"endpoint.advanced.openvpn.sections.communication.header" = "Komunikacja"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Jeśli masz problemy z połączeniem po zmianie konfiguracji komunikacji, kliknij żeby przywrócić domyślną konfigurację."; -"endpoint.advanced.openvpn.sections.compression.header" = "Kompresja"; -"endpoint.advanced.openvpn.sections.network.header" = "Sieć"; -"endpoint.advanced.openvpn.sections.other.header" = "Inne"; -"endpoint.advanced.openvpn.items.route.caption" = "Trasowanie"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Szyfr"; -"endpoint.advanced.openvpn.items.digest.caption" = "Uwierzytelnienie"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Osadzony"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Struktura"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algorytm"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Nieobsługiwane"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Zresetuj konfigurację"; -"endpoint.advanced.openvpn.items.client.caption" = "Certyfikat"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Klucz"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Zweryfikowany"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Niezweryfikowany"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Uwierzytelnienie"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Szyfrowanie"; -"endpoint.advanced.openvpn.items.eku.caption" = "Rozszerzona weryfikacja"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d sekund"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Ponowna negocjacja"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "po %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Losowy host końcowy"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Randomizuj nazwy hostów"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Ustawienia sieci"; -"network_settings.sections.choices.header" = "Nadpisz"; -"network_settings.gateway.title" = "Domyślna brama sieciowa"; -"network_settings.proxy.items.bypass_domains.caption" = "Pomiń domeny"; -"network_settings.items.add_dns_server.caption" = "Dodaj adres"; -"network_settings.items.add_dns_domain.caption" = "Dodaj domenę wyszukiwania"; -"network_settings.items.proxy_bypass.caption" = "Pomiń domenę"; -"network_settings.items.add_proxy_bypass.caption" = "Dodaj domenę"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "Na żądanie"; -"on_demand.sections.policy.footer" = "Aktywuj VPN %@."; -"on_demand.items.add_ssid.caption" = "Dodaj Wi-Fi"; -"on_demand.items.active.caption" = "Ufaj"; -"on_demand.items.mobile.caption" = "Sieć komórkowa"; -"on_demand.items.ethernet.caption" = "Połączenia przewodowe"; - -"on_demand.items.policy.caption" = "Wyłącz VPN dla zaufanych sieci"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnostyka"; -"diagnostics.sections.debug_log.footer" = "Status maskowania będzie widoczny po ponownym połączeniu. Dane połączenia to nazwy hostów, adresy IP, routing, SSID. Loginy i klucze prywatne nie są zapisywane."; -"diagnostics.items.server_configuration.caption" = "Konfiguracja serwera"; -"diagnostics.items.app_log.title" = "Aplikacja"; -"diagnostics.items.masks_private_data.caption" = "Maskuj dane sieci"; -"diagnostics.items.report_issue.caption" = "Zgłoś problemy z połączeniem"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Aby bezpiecznie zresetować rejestr debugowania i zastosować nowe ustawienia maskowania, musisz połączyć się z VPN ponownie."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Debugowanie"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Kopiuj"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Zgłoś błąd"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Dodaj skrót"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Dane komórkowe"; -"shortcuts.add.items.connect.caption" = "Połącz z"; -"shortcuts.add.items.enable_vpn.caption" = "Włącz VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Wyłącz VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Zaufaj obecnie połączonej sieci Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Przestań ufać obecnie połączonej sieci Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Ufaj danym komórkowym"; -"shortcuts.add.items.untrust_cellular.caption" = "Przestań ufać danym komórkowym"; -"shortcuts.add.alerts.no_profiles.message" = "Brak wybranego profilu połączenia."; - -"shortcuts.edit.title" = "Zarządzaj skrótami"; -"shortcuts.edit.sections.all.header" = "Istniejące skróty"; -"shortcuts.edit.sections.add.footer" = "Użyj Siri żeby przyspieszyć najczęstsze akcje w aplikacji."; -"shortcuts.edit.items.add_shortcut.caption" = "Dodaj skrót"; - -/* MARK: PaywallView */ - -"paywall.title" = "Kup"; -"paywall.sections.products.footer" = "Każdy produkt to zakup jednorazowy. Kuipno usługodawcy nie zawiera subskrypcji VPN."; -"paywall.items.loading.caption" = "Ładowanie produktów"; -"paywall.items.full_version.extra_description" = "Wszyscy usługodawcy (włączając przyszłych)\n%@"; -"paywall.items.restore.title" = "Przywróć zakup"; -"paywall.items.restore.description" = "Jeśli kupiłeś tą aplikację lub funkcję wcześniej, możesz przywrócić swoje zakupy i ten ekran nie będzie wyświetlony ponownie."; -"paywall.alerts.purchase.appletv.success.message" = "Dziękujemy! Limit czasu zostanie zniesiony, gdy tylko iCloud zaktualizuje dane. Odczekaj chwilę, a następnie ponownie uruchom połączenie w aplikacji TV."; - -/* MARK: DonateView */ - -"donate.title" = "Dotacja"; -"donate.sections.one_time.header" = "Jeden raz"; -"donate.sections.one_time.footer" = "Jeśli chcesz docenić moją pracę, poniżej znajdziesz kilka kwot do wyboru dotacji.\n\nTwoje konto zostanie obciążone tylko raz na jedną dotację, możesz wysłać dotację kilka razy."; -"donate.items.loading.caption" = "Ładowanie dotacji"; -"donate.items.purchasing.caption" = "Wykonywanie dotacji"; -"donate.alerts.purchase.success.message" = "To dla mnie dużo znaczy, mam nadzięję że będziesz używać aplikacji i przyczynisz się do jej rozpowrzechnienia."; -"donate.alerts.purchase.failure.message" = "Nie można dokonać dotacji. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Ustawienia"; -"settings.items.locks_in_background.caption" = "Zablokuj dostęp do aplikacji"; -"settings.items.donate.caption" = "Wyślij dotację"; - -/* MARK: AboutView */ - -"about.title" = "O programie"; -"about.sections.web.header" = "Strona WWW"; -"about.items.credits.caption" = "Twórcy"; -"about.items.join_community.caption" = "Dołącz do społeczności"; -"about.items.write_review.caption" = "Napisz recenzję"; -"about.items.share_twitter.caption" = "Wyślij tweeta!"; -"about.items.website.caption" = "Strona domowa"; -"about.items.disclaimer.caption" = "Zastrzeżenie"; -"about.items.privacy_policy.caption" = "Polityka prywatności"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Wersja"; -"version.labels.intro" = "Passepartout i TunnelKit są stworzone i utrzymywane przez Davide De Rosa (keeshux).\n\nKod źródłowy Passepartout i TunnelKit jest publicznie dostępny na licencji GPLv3, linki możesz znaleźć na stronie domowej."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Twórcy"; -"credits.sections.licenses.header" = "Licencje"; -"credits.sections.notices.header" = "Dodatki"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Preferencje"; -"preferences.sections.general.header" = "Ogólne"; -"preferences.items.launches_on_login.caption" = "Uruchom po zalogowaniu"; -"preferences.items.launches_on_login.footer" = "Zaznacz, aby automatycznie uruchamiać aplikację przy restarcie systemu lub logowaniu."; -"preferences.items.confirm_quit.caption" = "Potwierdź zakończenie pracy"; -"preferences.items.confirm_quit.footer" = "Zaznacz, aby wyświetlić monit o potwierdzeniu zakończenia."; diff --git a/Passepartout/App/pt.lproj/InfoPlist.strings b/Passepartout/App/pt.lproj/InfoPlist.strings deleted file mode 100644 index f8a7bcc1..00000000 --- a/Passepartout/App/pt.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Aceder ao nome da rede Wi-Fi atual"; - -"NSFaceIDUsageDescription" = "Desbloquear aplicação com o Face ID"; diff --git a/Passepartout/App/pt.lproj/Localizable.strings b/Passepartout/App/pt.lproj/Localizable.strings deleted file mode 100644 index 08a43467..00000000 --- a/Passepartout/App/pt.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Política"; -"global.strings.networks" = "Redes"; -"global.strings.edit" = "Editar"; -"global.strings.unknown" = "Desconhecido"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Por favor, escolha um ponto final"; - -"on_demand.sections.policy.footer.any" = "em qualquer rede"; -"on_demand.sections.policy.footer.including" = "apenas nas redes abaixo"; -"on_demand.sections.policy.footer.excluding" = "exceto nas redes abaixo"; -"on_demand.policy.any" = "Todas as redes"; -"on_demand.policy.including" = "Incluir"; -"on_demand.policy.excluding" = "Excluir"; - -"settings.sections.icloud.footer" = "Desative a sincronizar para permitir apagar. Para apagar a loja iCloud de forma segura, faça-o em todos os seus dispositivos sincronizados. Isto não irá afetar os perfis locais"; -"settings.items.should_enable_cloud_syncing.caption" = "Sincronizar com iCloud"; -"settings.items.erase_cloud_store.caption" = "Apagar loja da iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Cancelar"; -"global.strings.next" = "Próximo"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Guardar"; -"global.strings.rename" = "Renomear"; -"global.strings.duplicate" = "Duplicar"; -"global.strings.add" = "Adicionar"; -"global.strings.delete" = "Apagar"; -"global.strings.uninstall" = "Desinstalar"; -"global.strings.default" = "Default"; -"global.strings.name" = "Nome"; -"global.strings.profiles" = "Perfis"; -"global.strings.provider" = "Provedor"; -"global.strings.providers" = "Provedores"; -"global.strings.configuration" = "Configuração"; -"global.strings.address" = "Endereço"; -"global.strings.addresses" = "Endereços"; -"global.strings.port" = "Porta"; -"global.strings.protocol" = "Protocolo"; -"global.strings.protocols" = "Protocolos"; -"global.strings.enabled" = "Ativado"; -"global.strings.disabled" = "Desativado"; -"global.strings.none" = "Nenhum"; -"global.strings.automatic" = "Automático"; -"global.strings.manual" = "Manual"; -"global.strings.encryption" = "Criptografia"; -"global.strings.reconnect" = "Reconectar"; -"global.strings.servers" = "Servidores"; -"global.strings.domain" = "Domínio"; -"global.strings.domains" = "Domínios"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Interface"; -"global.strings.private_key" = "Chave privada"; -"global.strings.public_key" = "Chave pública"; -"global.strings.endpoint" = "Destino"; -"global.strings.keepalive" = "Manter ativo"; -"global.strings.advanced" = "Avançado"; -"global.strings.translations" = "Traduções"; -"global.strings.show" = "Mostrar"; -"global.strings.connect" = "Ligar"; -"global.strings.disconnect" = "Desligar"; -"global.strings.download" = "Transferir"; -"global.strings.authentication" = "Autenticação"; -"global.messages.unlock_app" = "O Passepartout está bloqueado"; -"global.messages.email_not_configured" = "Nenhuma conta de email configurada."; -"global.messages.share" = "Passepartout é um cliente OpenVPN / WireGuard fácil e open-source para iOS e macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Lembrar-me depois"; -"global.alerts.buttons.never" = "Não perguntar novamente"; - -"global.placeholders.profile_name" = "Meu perfil"; - -"global.errors.missing_profile" = "Perfil em falta"; -"global.errors.missing_account" = "Conta em falta"; -"global.errors.missing_provider_server" = "Localização em falta"; -"global.errors.missing_provider_preset" = "Pré-definição em falta"; -"global.errors.tunnel_expired" = "A ligação expirou"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Suporte"; -"menu.all.share.title" = "Compartilhar"; -"menu.all.about.title" = "Sobre %@"; - -"menu.contextual.add_profile.from_files" = "De ficheiros"; -"menu.contextual.add_profile.from_text" = "De texto"; -"menu.contextual.add_profile.imported" = "Adicionar %@"; -"menu.contextual.support.write_review" = "Avaliação"; -"menu.contextual.support.join_community" = "Comunidade"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Convite"; - -"menu.system.quit.title" = "Sair %@"; - -"menu.system.quit.messages.confirm" = "A VPN, se ativa, ainda vai ser executada em segundo plano. Quer sair?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Conectando"; -"tunnelkit.vpn.active" = "Ativa"; -"tunnelkit.vpn.disconnecting" = "Desconectando"; -"tunnelkit.vpn.inactive" = "Inativo"; -"tunnelkit.vpn.disabled" = "Desativado"; -"tunnelkit.vpn.unused" = "Desativado"; - -"tunnelkit.errors.vpn.timeout" = "Timeout"; -"tunnelkit.errors.vpn.dns" = "Falha no DNS"; -"tunnelkit.errors.vpn.auth" = "Falha na autenticação"; -"tunnelkit.errors.vpn.tls" = "Falha no TLS"; -"tunnelkit.errors.vpn.encryption" = "Falha na criptografia"; -"tunnelkit.errors.vpn.compression" = "Compressão não suportada"; -"tunnelkit.errors.vpn.network" = "Rede alterada"; -"tunnelkit.errors.vpn.routing" = "Rota necessária"; -"tunnelkit.errors.vpn.gateway" = "Sem gateway"; -"tunnelkit.errors.vpn.shutdown" = "Servidor desligado"; - -"tunnelkit.errors.parsing" = "Não foi possível processar as configurações do arquivo (%@)."; -"tunnelkit.errors.openvpn.malformed" = "O arquivo de configuração possui uma opção não formatada corretamente (%@)."; -"tunnelkit.errors.openvpn.required_option" = "O arquivo não possui todas configurações requeridas (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "O arquivo de configuração possui uma opção não suportada (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "O arquivo de configuração está correto, mas provavelmente possui uma opção não suportada (%@).\n\nSua conexão poderá ser instável dependendo as configurações do servidor."; -"tunnelkit.errors.openvpn.passphrase_required" = "Por favor, digite sua senha de criptografia."; -"tunnelkit.errors.openvpn.decryption" = "Sua configiração possui uma chave privada criptografada que talvez não possa ser descriptografada. Verifique novamente sua senha de criptografia."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "Ativo"; -"organizer.sections.tv.profiles_list.header.p1" = "Abra o Passepartout no seu dispositivo iOS ou macOS e ative o botão \"Apple TV\" de um perfil para o fazer aparecer aqui."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Sem perfis"; - -"organizer.alerts.reddit.message" = "Você sabia que Passepartout tem um subreddit? Siga-nos para atualizações ou para discutir problemas, novas funcionalidades, ou qualquer outro tópico.\n\nÉ uma boa maneira de mostrar seu interesse pelo projeto."; -"organizer.alerts.reddit.buttons.subscribe" = "Seguir!"; - -"organizer.alerts.remove_profile.message" = "Tem a certeza de que pretende eliminar o perfil %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Perfil novo"; -"add_profile.shared.views.existing.header" = "Perfis existentes"; -"add_profile.shared.alerts.overwrite.message" = "Já existe um perfil com o mesmo nome. Deseja substituí-lo?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Introduza a sua senha de criptografia"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Aqui você encontra um provedor com o perfil pré-configurado."; -"add_profile.provider.items.update_list" = "Atualizar lista"; -"add_profile.provider.errors.no_default_server" = "Impossível encontrar um servidor."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Bem-vindo ao Passepartout!\n\nUse o organizador para adicionar um novo perfil."; - -"profile.sections.vpn.footer" = "A conexão será estabelecida assim que necessária."; -"profile.sections.status.header" = "Conexão"; -"profile.sections.provider_infrastructure.footer" = "Última atualização em %@."; -"profile.sections.tv.footer.encryption" = "Os perfis estão encriptados e estão disponíveis para a sua Apple TV através da iCloud."; -"profile.sections.tv.footer.restricted.p1" = "Todavia, a ligação vai perder a validade depois de %d minutos."; -"profile.sections.tv.footer.restricted.p2" = "Compre para largar a restrição."; -"profile.sections.vpn_survives_sleep.footer" = "Desative para melhorar o consumo de bateria, o que poderá ocasionar queda de performance quando o restabelecimento de conexão for realizado."; -"profile.sections.vpn_resolves_hostname.footer" = "Recomendado para maioria das redes e requirido em algumas redes IPv6. Desative se o DNS estiver bloqueado, ou para acelerar o DNS quando o mesmo está devagar."; -"profile.sections.feedback.header" = "Feedback"; -"profile.items.use_profile.caption" = "Usar esse perfil"; -"profile.items.vpn_service.caption" = "Ativado"; -"profile.items.vpn.turn_on.caption" = "Ativar VPN"; -"profile.items.vpn.turn_off.caption" = "Desativar VPN"; -"profile.items.connection_status.caption" = "Status"; -"profile.items.data_count.caption" = "Dados transferidos"; -"profile.items.randomizes_server.caption" = "Servidor aleatório"; -"profile.items.provider.refresh.caption" = "Atualizar infraestrutura"; -"profile.items.category.caption" = "Categoria"; -"profile.items.only_shows_favorites.caption" = "Mostrar apenas os locais preferidos"; -"profile.items.vpn_survives_sleep.caption" = "Manter ativo em modo descanço"; -"profile.items.vpn_resolves_hostname.caption" = "Resolver hostname do servidor"; -"profile.items.tv_sharing.caption.limited" = "Limitado a %d minutos"; -"profile.items.expires_at.caption" = "Validade"; - -"profile.alerts.rename.title" = "Renomear perfil"; -"profile.alerts.reconnect_vpn.message" = "Deseja reconectar à VPN?"; -"profile.alerts.uninstall_vpn.message" = "Tem certeza que deseja remover as configurações de VPN do seu dispositivo? Isso poderá corrigir problemas com o estado atual, sem afetar seu provedor e perfis do host."; -"profile.alerts.test_connectivity.title" = "Conectividade"; -"profile.alerts.test_connectivity.messages.success" = "Seu dispositivo está conectado à Internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Seu dispositivo não está conectado à Internet, por favor, verifique sua configurações."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Localização"; -"provider.location.sections.empty_favorites.footer" = "Deslize para a esquerda em um local para adicioná-lo ou removê-lo dos Favoritos."; -"provider.location.actions.favorite" = "Favorito"; -"provider.location.actions.unfavorite" = "Não favorito"; - -"provider.preset.title" = "Pré-definição"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Conta"; -"account.sections.credentials.header" = "Credenciais"; -"account.sections.registration.footer" = "Registrar em %@ website."; -"account.items.authentication_method.persistent" = "Persistente"; -"account.items.authentication_method.interactive" = "Interativo"; -"account.items.username.caption" = "Usuário"; -"account.items.username.placeholder" = "usuário"; -"account.items.password.caption" = "Senha"; -"account.items.password.placeholder" = "senha secreta"; -"account.items.seed.caption" = "Seed"; -"account.items.open_guide.caption" = "Ver sua credenciais"; -"account.items.signup.caption" = "Registrar com %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Utilize %@ credenciais do site."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Utilize suas credenciais de serviço %@, que podem diferir das credenciais do site."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Utilize %@ credenciais do site. Seu usuário é normalmente numérico (sem espaços)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Utilize %@ credenciais do site. Seu usuário é normalmente o seu email."; -"account.sections.guidance.footer.infrastructure.pia" = "Utilize %@ credenciais do site. Seu usuário é normalmente numérico com prefixo \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Encontre %@ credenciais na sessão \"Account > OpenVPN / IKEv2 Username\" do site."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Utilize %@ credenciais do site. Seu usuário é normalmente o seu email."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Utilize %@ credenciais do site. Seu usuário é normalmente o seu email."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Encontre %@ credenciais no gerador de configuração OpenVPN do site."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Ponto"; -"endpoint.wireguard.items.preshared_key.caption" = "Chave pré-partilhada"; -"endpoint.wireguard.items.allowed_ip.caption" = "IP Permitido"; - -"endpoint.advanced.title" = "Detalhes técnicos"; -"endpoint.advanced.openvpn.sections.pull.header" = "Puxar do servidor"; -"endpoint.advanced.openvpn.sections.communication.header" = "Comunicação"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Se você foi desconectado após mudar parâmetros de comunicação, toque para restaurar a configuração original."; -"endpoint.advanced.openvpn.sections.compression.header" = "Compressão"; -"endpoint.advanced.openvpn.sections.network.header" = "Rede"; -"endpoint.advanced.openvpn.sections.other.header" = "Outro"; -"endpoint.advanced.openvpn.items.route.caption" = "Rota"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Criptografada"; -"endpoint.advanced.openvpn.items.digest.caption" = "Autenticação"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Agregado"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Framing"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algorítimo"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Não suportado"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Restaurar configuração"; -"endpoint.advanced.openvpn.items.client.caption" = "Certificado"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Chave"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Verificado"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Não verificado"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Wrapping"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Autenticação"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Criptografia"; -"endpoint.advanced.openvpn.items.eku.caption" = "Verificação extendida"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d segundos"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Renegociando"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "depois de %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Destino randômico"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Nomes de anfitrião aleatórios"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Configurações de rede"; -"network_settings.sections.choices.header" = "Substituir"; -"network_settings.gateway.title" = "Gateway padrão"; -"network_settings.proxy.items.bypass_domains.caption" = "Fazer um bypass aos domínios"; -"network_settings.items.add_dns_server.caption" = "Adicionar endereço"; -"network_settings.items.add_dns_domain.caption" = "Adicionar domínio"; -"network_settings.items.proxy_bypass.caption" = "Domínio ignorado"; -"network_settings.items.add_proxy_bypass.caption" = "Adicionar domínio ignorado"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "A pedido"; -"on_demand.sections.policy.footer" = "Ativar a VPN %@."; -"on_demand.items.add_ssid.caption" = "Adicionar Wi-Fi"; -"on_demand.items.active.caption" = "Confiar"; -"on_demand.items.mobile.caption" = "Rede celular"; -"on_demand.items.ethernet.caption" = "Ligações com fios"; - -"on_demand.items.policy.caption" = "Trust disables VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnóstico"; -"diagnostics.sections.debug_log.footer" = "O status será escondido após reconectado. Os dados da rede são hostnames, endereços de IP, rotas, SSID. Credenciais e chaves privadas não será logadas em nenhum dos casos."; -"diagnostics.items.server_configuration.caption" = "Configuração do servidor"; -"diagnostics.items.app_log.title" = "Aplicativo"; -"diagnostics.items.masks_private_data.caption" = "Esconder dados da rede"; -"diagnostics.items.report_issue.caption" = "Reportar problemas de conexão"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Para garantir uma restauração segura do seu log de debug, você precisa reconectar à VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Log de Debug"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Copiar"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Reportar problema"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Adicionar atalho"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Celular"; -"shortcuts.add.items.connect.caption" = "Conectar à"; -"shortcuts.add.items.enable_vpn.caption" = "Ativar VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Desativar VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Confiar na Wi-Fi atual"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Não confiar na Wi-Fi atual"; -"shortcuts.add.items.trust_cellular.caption" = "Confiar em rede celular"; -"shortcuts.add.items.untrust_cellular.caption" = "Não confiar em rede celular"; -"shortcuts.add.alerts.no_profiles.message" = "Ainda não existe nenhum perfil para se conectar."; - -"shortcuts.edit.title" = "Configuração de atalhos"; -"shortcuts.edit.sections.all.header" = "Atalhos existentes"; -"shortcuts.edit.sections.add.footer" = "Peça ajuda para Siri para agilar tarefas comum do aplicativo."; -"shortcuts.edit.items.add_shortcut.caption" = "Adicionar atalho"; - -/* MARK: PaywallView */ - -"paywall.title" = "Comprar"; -"paywall.sections.products.footer" = "Todo produto é uma compra única. As compras do fornecedor não incluem uma assinatura VPN."; -"paywall.items.loading.caption" = "A carregar produtos"; -"paywall.items.full_version.extra_description" = "Todos os provedores (incluindo os futuros)\n%@"; -"paywall.items.restore.title" = "Restaurar compras"; -"paywall.items.restore.description" = "Se você comprou este aplicativo ou recurso no passado, pode restaurar suas compras e essa tela não será exibida novamente."; -"paywall.alerts.purchase.appletv.success.message" = "Obrigado! O limite de tempo irá desaparecer assim que a iCloud seja atualizada. Espere um momento e depois reinicie a ligação na aplicação da TV."; - -/* MARK: DonateView */ - -"donate.title" = "Doar"; -"donate.sections.one_time.header" = "Uma vez"; -"donate.sections.one_time.footer" = "Se você deseja mostrar gratidão pelo meu trabalho, aqui estão alguns valores do qual você pode contribuir.\n\nVocé só será cobrado uma única vez, ou doar mais vezes caso desejar."; -"donate.items.loading.caption" = "Carregando doações"; -"donate.items.purchasing.caption" = "Efetuando doação"; -"donate.alerts.purchase.success.message" = "Isso significa muito para mim! Espero que você continue usando e promovendo esse aplicativo."; -"donate.alerts.purchase.failure.message" = "Não foi possível realizar doação. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Ajustes"; -"settings.items.locks_in_background.caption" = "Bloquear acesso à aplicação"; -"settings.items.donate.caption" = "Fazer doação"; - -/* MARK: AboutView */ - -"about.title" = "Sobre"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Créditos"; -"about.items.join_community.caption" = "Participar da comunidade"; -"about.items.write_review.caption" = "Escrever avaliação"; -"about.items.share_twitter.caption" = "Tweet sobre isso!"; -"about.items.website.caption" = "Home page"; -"about.items.disclaimer.caption" = "Disclaimer"; -"about.items.privacy_policy.caption" = "Política de privacidade"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Versão"; -"version.labels.intro" = "Passepartout e TunnelKit são desenvolvidos e mantidos por Davide De Rosa (keeshux).\n\nO código de fonte está disponível no GitHub sobre a licença GPLv3, você pode encontrar links na home page."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Créditos"; -"credits.sections.licenses.header" = "Licenças"; -"credits.sections.notices.header" = "Notices"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Preferências"; -"preferences.sections.general.header" = "Geral"; -"preferences.items.launches_on_login.caption" = "Iniciar ao iniciar a sessão"; -"preferences.items.launches_on_login.footer" = "Assinale para executar automaticamente a aplicação ao arrancar ou com o início de sessão."; -"preferences.items.confirm_quit.caption" = "Confirmar a saída"; -"preferences.items.confirm_quit.footer" = "Assinale para apresentar um alerta de confirmação da saída."; diff --git a/Passepartout/App/ru.lproj/InfoPlist.strings b/Passepartout/App/ru.lproj/InfoPlist.strings deleted file mode 100644 index e62db4fc..00000000 --- a/Passepartout/App/ru.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Доступ к имени подключенной сети Wi-Fi"; - -"NSFaceIDUsageDescription" = "Разблокировать приложение по Face ID"; diff --git a/Passepartout/App/ru.lproj/Localizable.strings b/Passepartout/App/ru.lproj/Localizable.strings deleted file mode 100644 index 5d955777..00000000 --- a/Passepartout/App/ru.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Политика"; -"global.strings.networks" = "Сети"; -"global.strings.edit" = "Редактировать"; -"global.strings.unknown" = "Неизвестно"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Выберите конечную точку"; - -"on_demand.sections.policy.footer.any" = "в любой сети"; -"on_demand.sections.policy.footer.including" = "только в сетях ниже"; -"on_demand.sections.policy.footer.excluding" = "кроме сетей ниже"; -"on_demand.policy.any" = "Все сети"; -"on_demand.policy.including" = "Включить"; -"on_demand.policy.excluding" = "Исключить"; - -"settings.sections.icloud.footer" = "Отключите синхронизацию, чтобы разрешить удаление. Чтобы безопасно очистить хранилище iCloud, сделайте это на всех синхронизированных устройствах. Это не повлияет на локальные профили."; -"settings.items.should_enable_cloud_syncing.caption" = "Синхронизировать с iCloud"; -"settings.items.erase_cloud_store.caption" = "Очистить iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Отменить"; -"global.strings.next" = "Далее"; -/* MARK: Global */ -"global.strings.ok" = "Ок"; -"global.strings.save" = "Сохранить"; -"global.strings.rename" = "Переименовать"; -"global.strings.duplicate" = "Дублировать"; -"global.strings.add" = "Добавить"; -"global.strings.delete" = "Удалить"; -"global.strings.uninstall" = "Удалить приложение"; -"global.strings.default" = "По умолчанию"; -"global.strings.name" = "Имя"; -"global.strings.profiles" = "Профили"; -"global.strings.provider" = "Провайдер"; -"global.strings.providers" = "Провайдеры"; -"global.strings.configuration" = "Конфигурация"; -"global.strings.address" = "Адрес"; -"global.strings.addresses" = "Адреса"; -"global.strings.port" = "порт"; -"global.strings.protocol" = "Протокол"; -"global.strings.protocols" = "Протоколы"; -"global.strings.enabled" = "Включен"; -"global.strings.disabled" = "Выключен"; -"global.strings.none" = "Нет"; -"global.strings.automatic" = "Автоматически"; -"global.strings.manual" = "Вручную"; -"global.strings.encryption" = "Шифрование"; -"global.strings.reconnect" = "Переподключить"; -"global.strings.servers" = "Серверы"; -"global.strings.domain" = "Домен"; -"global.strings.domains" = "Домены"; -"global.strings.proxy" = "Прокси"; -"global.strings.bytes" = "байты"; -"global.strings.interface" = "Интерфейс"; -"global.strings.private_key" = "Закрытый ключ"; -"global.strings.public_key" = "Открытый ключ"; -"global.strings.endpoint" = "Конечная точка"; -"global.strings.keepalive" = "Поддерживаем"; -"global.strings.advanced" = "Расширенные"; -"global.strings.translations" = "Переводы"; -"global.strings.show" = "Показать"; -"global.strings.connect" = "Подключиться"; -"global.strings.disconnect" = "Отключиться"; -"global.strings.download" = "Загрузить"; -"global.strings.authentication" = "Аутентификация"; -"global.messages.unlock_app" = "Passepartout заблокирован"; -"global.messages.email_not_configured" = "E-mail аккаунт не создан."; -"global.messages.share" = "Passepartout — это удобный в использовании клиент OpenVPN / WireGuard для iOS и macOS с открытым исходным кодом"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Напомнить позже"; -"global.alerts.buttons.never" = "Больше не спрашивать"; - -"global.placeholders.profile_name" = "Мой профиль"; - -"global.errors.missing_profile" = "Отсутствует профиль"; -"global.errors.missing_account" = "Отсутствует учетная запись"; -"global.errors.missing_provider_server" = "Отсутствует местоположение"; -"global.errors.missing_provider_preset" = "Отсутствует пресет"; -"global.errors.tunnel_expired" = "Срок действия соединения истек"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Поддержка"; -"menu.all.share.title" = "Поделиться"; -"menu.all.about.title" = "Об %@"; - -"menu.contextual.add_profile.from_files" = "Из файлов"; -"menu.contextual.add_profile.from_text" = "Из текста"; -"menu.contextual.add_profile.imported" = "Добавить %@"; -"menu.contextual.support.write_review" = "Отзыв"; -"menu.contextual.support.join_community" = "Сообщество"; -"menu.contextual.share_twitter" = "Твит"; -"menu.contextual.share_generic" = "Пригласить"; - -"menu.system.quit.title" = "Выйти из %@"; - -"menu.system.quit.messages.confirm" = "Если включить VPN, он всё равно будет работать в фоновом режиме. Вы точно хотите выйти?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Подключается"; -"tunnelkit.vpn.active" = "Активен"; -"tunnelkit.vpn.disconnecting" = "Отключается"; -"tunnelkit.vpn.inactive" = "Не активен"; -"tunnelkit.vpn.disabled" = "Отключен"; -"tunnelkit.vpn.unused" = "Выкл"; - -"tunnelkit.errors.vpn.timeout" = "Тайм-аут"; -"tunnelkit.errors.vpn.dns" = "Ошибка DNS"; -"tunnelkit.errors.vpn.auth" = "Ошибка аутентификации"; -"tunnelkit.errors.vpn.tls" = "Ошибка TSL"; -"tunnelkit.errors.vpn.encryption" = "Ошибка расшифровки"; -"tunnelkit.errors.vpn.compression" = "Сжатие не поддерживается"; -"tunnelkit.errors.vpn.network" = "Изменение сети"; -"tunnelkit.errors.vpn.routing" = "Отсутствует маршрутизация"; -"tunnelkit.errors.vpn.gateway" = "Нет шлюза"; -"tunnelkit.errors.vpn.shutdown" = "Сервер выключен"; - -"tunnelkit.errors.parsing" = "Не получается разобрать предоставленный файл конфигурации (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Файл конфигурации содержит неверную опцию (%@)."; -"tunnelkit.errors.openvpn.required_option" = "Файл конфигурации не содержит необходимую опцию (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Файл конфигурации содержит неподдерживаемую опцию (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Файл конфигурации верный, но возможно содержит неподдерживаемую опцию (%@).\n\nСоединение может прерваться - зависит от настроек сервера."; -"tunnelkit.errors.openvpn.passphrase_required" = "Пожалуйста, введите кодовую фразу шифрования"; -"tunnelkit.errors.openvpn.decryption" = "Конфигурация содержит зашифрованный приватный ключ, он не может быть расшифрован. Перепроверьте кодовую фразу."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "Используется"; -"organizer.sections.tv.profiles_list.header.p1" = "Откройте Passepartout на своем устройстве iOS или macOS и включите Apple TV в профиле, чтобы он появился здесь."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Нет профилей"; - -"organizer.alerts.reddit.message" = "А Вы знали, что Passepartout имеет свой сабреддит? Подписывайтесь для получения обновлений, обсуждения проблем, функций, новых платформ или чего угодно.\n\nЭто также отличный способ показать поддержку проекта."; -"organizer.alerts.reddit.buttons.subscribe" = "Подписаться сейчас!"; - -"organizer.alerts.remove_profile.message" = "Вы точно хотите удалить профиль %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Новый профиль"; -"add_profile.shared.views.existing.header" = "Существующие профили"; -"add_profile.shared.alerts.overwrite.message" = "Профиль с этим именем уже существует. Заменить?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Введите кодовую фразу"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Здесь Вы найдёте несколько провайдеров с уже созданными профилями."; -"add_profile.provider.items.update_list" = "Обновить список"; -"add_profile.provider.errors.no_default_server" = "Не удалось найти сервер."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Добро пожаловать в Passepartout!\n\nИспользуйте организатор для добавления нового профиля."; - -"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" = "Отзыв"; -"profile.items.use_profile.caption" = "Использовать это профиль."; -"profile.items.vpn_service.caption" = "Включен"; -"profile.items.vpn.turn_on.caption" = "Включить VPN"; -"profile.items.vpn.turn_off.caption" = "Отключить VPN"; -"profile.items.connection_status.caption" = "Статус"; -"profile.items.data_count.caption" = "Переданная информация"; -"profile.items.randomizes_server.caption" = "Выбрать случайный сервер"; -"profile.items.provider.refresh.caption" = "Обновить инфраструктуру"; -"profile.items.category.caption" = "Категория"; -"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?"; -"profile.alerts.uninstall_vpn.message" = "Вы действительно хотите убрать VPN конфигурацию из настроек устройства? Это может исправить несколько VPN ошибок, но не изменит установки приложения."; -"profile.alerts.test_connectivity.title" = "Связь"; -"profile.alerts.test_connectivity.messages.success" = "Ваше устройство подключено к интернету!"; -"profile.alerts.test_connectivity.messages.failure" = "Ваше устройство не подключено к интернету, пожалйста проверьте установки Вашего профиля."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Местоположение"; -"provider.location.sections.empty_favorites.footer" = "Свайп в лево на локации, чтобы добавить или убрать из избранного."; -"provider.location.actions.favorite" = "Добавить в избранное"; -"provider.location.actions.unfavorite" = "Убрать из избранного"; - -"provider.preset.title" = "Пресет"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Аккаунт"; -"account.sections.credentials.header" = "Данные для входа"; -"account.sections.registration.footer" = "Создайте аккаунт на %@ веб-сайте."; -"account.items.authentication_method.persistent" = "Постоянный"; -"account.items.authentication_method.interactive" = "Интерактивный"; -"account.items.username.caption" = "Логин"; -"account.items.username.placeholder" = "логин"; -"account.items.password.caption" = "Пароль"; -"account.items.password.placeholder" = "пароль"; -"account.items.seed.caption" = "Сид"; -"account.items.open_guide.caption" = "Проверьте Ваши данные"; -"account.items.signup.caption" = "Зарегистрируйтесь с %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Используйте Ваши данные для входа с веб-сайта %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Используйте свои учетные данные %@ service, которые могут отличаться от учетных данных веб-сайта."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Используйте Ваши данные для входа с веб-сайта %@. Ваш логин обычно числовой с (без пробелов)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Используйте данные для входа на %@ веб-сайт. Ваш логин обычно Ваш e-mail."; -"account.sections.guidance.footer.infrastructure.pia" = "Используйте Ваши данные для входа с веб-сайта %@. Ваш логин обычно числовой с приставкой \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Найдите Ваши данные для входа %@ \"Account > OpenVPN / IKEv2 Username\" секции веб-сайта."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Используйте данные для входа на %@ веб-сайт. Ваш логин обычно Ваш e-mail."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Используйте данные для входа на %@ веб-сайт. Ваш логин обычно Ваш e-mail."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Найдите Ваши данные для входа %@ в OpenVPN Config Generator на веб-сайте."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Узел"; -"endpoint.wireguard.items.preshared_key.caption" = "Общий ключ"; -"endpoint.wireguard.items.allowed_ip.caption" = "Допустимый IP"; - -"endpoint.advanced.title" = "Техническая информация"; -"endpoint.advanced.openvpn.sections.pull.header" = "Вытащить с сервера"; -"endpoint.advanced.openvpn.sections.communication.header" = "Связь"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Если после изменения параметров связи у Вас разорвалось соединение, нажмите, чтобы вернуться к исходной конфигурации."; -"endpoint.advanced.openvpn.sections.compression.header" = "Компресия"; -"endpoint.advanced.openvpn.sections.network.header" = "Сеть"; -"endpoint.advanced.openvpn.sections.other.header" = "Другое"; -"endpoint.advanced.openvpn.items.route.caption" = "Маршрут"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Шифруем"; -"endpoint.advanced.openvpn.items.digest.caption" = "Аутентификация"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Внедрена"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Фрейминг"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Алгоритм"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Неподдерживаемое"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Сброс конфигурации"; -"endpoint.advanced.openvpn.items.client.caption" = "Сертификат"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Ключ"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Проверено"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Не проверено"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Упаковываем"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Аутентификация"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Шифрование"; -"endpoint.advanced.openvpn.items.eku.caption" = "Расширенная проверка"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d секунд"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Перезаключение"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "после %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Рандомная конечная точка"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Выбирать имена хостов случайным образом"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Сетевые настройки"; -"network_settings.sections.choices.header" = "Игнорировать"; -"network_settings.gateway.title" = "Шлюз по умолчанию"; -"network_settings.proxy.items.bypass_domains.caption" = "Обходные домены"; -"network_settings.items.add_dns_server.caption" = "Добавить адрес"; -"network_settings.items.add_dns_domain.caption" = "Добавить домен поиска"; -"network_settings.items.proxy_bypass.caption" = "Обход домена"; -"network_settings.items.add_proxy_bypass.caption" = "Добавить обходной домен"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "По требованию"; -"on_demand.sections.policy.footer" = "Активировать VPN %@."; -"on_demand.items.add_ssid.caption" = "Добавить Wi-Fi"; -"on_demand.items.active.caption" = "Доверенные"; -"on_demand.items.mobile.caption" = "Мобильная сеть"; -"on_demand.items.ethernet.caption" = "Проводные подключения"; - -"on_demand.items.policy.caption" = "Дов. сеть отключает VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Диагностика"; -"diagnostics.sections.debug_log.footer" = "Маскировка включится после повторного подключения. Информация о сети - это названия хост профилей, IP адрес, маршрутизация и SSID. Данные для входа и приватные ключи не собираются."; -"diagnostics.items.server_configuration.caption" = "Конфигурация сервера"; -"diagnostics.items.app_log.title" = "Приложение"; -"diagnostics.items.masks_private_data.caption" = "Маскировать информацию сети"; -"diagnostics.items.report_issue.caption" = "Сообщить о проблеме подкл."; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Для безопасного сброса журнала отладки и изменения маскировки информации сети Вы должны заново подключиться к VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Журнал отладки"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Копировать"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Сообщить о проблеме"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Создать команду"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Мобильная сеть"; -"shortcuts.add.items.connect.caption" = "Подключиться к"; -"shortcuts.add.items.enable_vpn.caption" = "Включи VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Выключи VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Доверять текущему Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Не доверять текущему Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Доверять мобильной сети"; -"shortcuts.add.items.untrust_cellular.caption" = "Не доверять мобильной сети"; -"shortcuts.add.alerts.no_profiles.message" = "Нет профиля для подключения."; - -"shortcuts.edit.title" = "Управлять командами"; -"shortcuts.edit.sections.all.header" = "Существующие команды"; -"shortcuts.edit.sections.add.footer" = "Получить помощь Сири, чтобы ускорить частые действия с приложением."; -"shortcuts.edit.items.add_shortcut.caption" = "Создать команду"; - -/* MARK: PaywallView */ - -"paywall.title" = "Покупка"; -"paywall.sections.products.footer" = "Каждый продукт является разовой покупкой. Покупка провайдера не включает подписку на VPN."; -"paywall.items.loading.caption" = "Загрузка продуктов"; -"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 */ - -"donate.title" = "Пожертвовать"; -"donate.sections.one_time.header" = "Один раз"; -"donate.sections.one_time.footer" = "Если Вы хотите поблагодарить мою бесплатную работу, здесь есть несколько сумм, которые Вы можете пожертвовать прямо сейчас.\n\nСумма будет списана только один раз, а Вы можете пожертвовать несколько раз."; -"donate.items.loading.caption" = "Загружаем пожертвования"; -"donate.items.purchasing.caption" = "Исполняется"; -"donate.alerts.purchase.success.message" = "Это значит многое для меня, и, я надеюсь, Вы продолжить использовать и рассказывать об этом приложении."; -"donate.alerts.purchase.failure.message" = "Не получается совершить пожертвование. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Настройки"; -"settings.items.locks_in_background.caption" = "Заблокировать доступ приложения"; -"settings.items.donate.caption" = "Сделать пожертвование"; - -/* MARK: AboutView */ - -"about.title" = "О нас"; -"about.sections.web.header" = "Веб"; -"about.items.credits.caption" = "Благодарности"; -"about.items.join_community.caption" = "Вступить в сообщество"; -"about.items.write_review.caption" = "Написать отзыв"; -"about.items.share_twitter.caption" = "Твитнуть о нас!"; -"about.items.website.caption" = "Домашняя страница"; -"about.items.disclaimer.caption" = "Предупреждение"; -"about.items.privacy_policy.caption" = "Политика конфиденциальности"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Версия"; -"version.labels.intro" = "Passepartout и TunnelKit написаны и установлены Davide De Rosa (keeshux).\n\nИсходные коды для Passepartout и TunnelKit публично доступны на GitHub под GPLv3, вы можете найти ссылки на домашней странице."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Благодарность"; -"credits.sections.licenses.header" = "Лицензии"; -"credits.sections.notices.header" = "Упоминания"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Настройки"; -"preferences.sections.general.header" = "Общие"; -"preferences.items.launches_on_login.caption" = "Запускать при входе"; -"preferences.items.launches_on_login.footer" = "Включите, чтобы приложение автоматически запускалось при загрузке или входе."; -"preferences.items.confirm_quit.caption" = "Подтверждать выход"; -"preferences.items.confirm_quit.footer" = "Включите, чтобы выход надо было подтверждать."; diff --git a/Passepartout/App/sv.lproj/InfoPlist.strings b/Passepartout/App/sv.lproj/InfoPlist.strings deleted file mode 100644 index 68a678b1..00000000 --- a/Passepartout/App/sv.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Åtkomstnamn för aktuellt Wi-Fi"; - -"NSFaceIDUsageDescription" = "Lås upp appen med Face ID"; diff --git a/Passepartout/App/sv.lproj/Localizable.strings b/Passepartout/App/sv.lproj/Localizable.strings deleted file mode 100644 index 03e66639..00000000 --- a/Passepartout/App/sv.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Policy"; -"global.strings.networks" = "Nätverk"; -"global.strings.edit" = "Redigera"; -"global.strings.unknown" = "Okänt"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Välj en ändpunkt"; - -"on_demand.sections.policy.footer.any" = "i alla nätverk"; -"on_demand.sections.policy.footer.including" = "enbart i nedanstående nätverk"; -"on_demand.sections.policy.footer.excluding" = "utom i nedanstående nätverk"; -"on_demand.policy.any" = "Alla nätverk"; -"on_demand.policy.including" = "Inkludera"; -"on_demand.policy.excluding" = "Uteslut"; - -"settings.sections.icloud.footer" = "Inaktivera synkronisering för att göra det möjligt att radera. För att radera iCloud-lagring på ett säkert sätt behöver du göra det på alla dina synkroniserade enheter. Detta påverkar inte lokala profiler."; -"settings.items.should_enable_cloud_syncing.caption" = "Synkronisera med iCloud"; -"settings.items.erase_cloud_store.caption" = "Radera iCloud-lagring"; - -/* MARK: Global */ - -"global.strings.cancel" = "Avbryt"; -"global.strings.next" = "Nästa"; -/* MARK: Global */ -"global.strings.ok" = "OK"; -"global.strings.save" = "Spara"; -"global.strings.rename" = "Byt namn"; -"global.strings.duplicate" = "Duplicera"; -"global.strings.add" = "Lägg till"; -"global.strings.delete" = "Radera"; -"global.strings.uninstall" = "Avinstallera"; -"global.strings.default" = "Default"; -"global.strings.name" = "Namn"; -"global.strings.profiles" = "Profiler"; -"global.strings.provider" = "Leverantör"; -"global.strings.providers" = "Leverantörer"; -"global.strings.configuration" = "Konfiguration"; -"global.strings.address" = "Adress"; -"global.strings.addresses" = "Adresser"; -"global.strings.port" = "Port"; -"global.strings.protocol" = "Protokoll"; -"global.strings.protocols" = "Protokoll"; -"global.strings.enabled" = "Aktiverad"; -"global.strings.disabled" = "Inaktiverad"; -"global.strings.none" = "Ingen"; -"global.strings.automatic" = "Automatiskt"; -"global.strings.manual" = "Manuellt"; -"global.strings.encryption" = "Kryptering"; -"global.strings.reconnect" = "Reconnect"; -"global.strings.servers" = "Servrar"; -"global.strings.domain" = "Domain"; -"global.strings.domains" = "Domäner"; -"global.strings.proxy" = "Proxy"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "Gränssnitt"; -"global.strings.private_key" = "Privat nyckel"; -"global.strings.public_key" = "Publik nyckel"; -"global.strings.endpoint" = "Slutpunkt"; -"global.strings.keepalive" = "hål-vid-liv"; -"global.strings.advanced" = "Avancerat"; -"global.strings.translations" = "Translations"; -"global.strings.show" = "Visa"; -"global.strings.connect" = "Anslut"; -"global.strings.disconnect" = "Koppla från"; -"global.strings.download" = "Ladda ner"; -"global.strings.authentication" = "Autentisering"; -"global.messages.unlock_app" = "Passepartout är låst"; -"global.messages.email_not_configured" = "Inget e-postkonto är konfigurerat."; -"global.messages.share" = "Passepartout är en användarvänlig öppen källkod OpenVPN / WireGuard klient för iOS och macOS"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "Påminn mig senare"; -"global.alerts.buttons.never" = "Fråga inte igen"; - -"global.placeholders.profile_name" = "Min profil"; - -"global.errors.missing_profile" = "Profil saknas"; -"global.errors.missing_account" = "Konto saknas"; -"global.errors.missing_provider_server" = "Plats saknas"; -"global.errors.missing_provider_preset" = "Förinställning saknas"; -"global.errors.tunnel_expired" = "Anslutningen gick ut"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Support"; -"menu.all.share.title" = "Dela"; -"menu.all.about.title" = "Om %@"; - -"menu.contextual.add_profile.from_files" = "Från filer"; -"menu.contextual.add_profile.from_text" = "Från text"; -"menu.contextual.add_profile.imported" = "Lägg till %@"; -"menu.contextual.support.write_review" = "Recension"; -"menu.contextual.support.join_community" = "Community"; -"menu.contextual.share_twitter" = "Tweet"; -"menu.contextual.share_generic" = "Bjud in"; - -"menu.system.quit.title" = "Lämna %@"; - -"menu.system.quit.messages.confirm" = "Om ett VPN är aktiverat kommer detta fortfarande att köra i bakgrunden. Vill du lämna?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Anslutning"; -"tunnelkit.vpn.active" = "Aktiv"; -"tunnelkit.vpn.disconnecting" = "Koppla från"; -"tunnelkit.vpn.inactive" = "Inaktiv"; -"tunnelkit.vpn.disabled" = "Inaktiverad"; -"tunnelkit.vpn.unused" = "Av"; - -"tunnelkit.errors.vpn.timeout" = "Timeout"; -"tunnelkit.errors.vpn.dns" = "DNS misslyckades"; -"tunnelkit.errors.vpn.auth" = "Auth failed"; -"tunnelkit.errors.vpn.tls" = "TLS misslyckades"; -"tunnelkit.errors.vpn.encryption" = "Kryptering misslyckades"; -"tunnelkit.errors.vpn.compression" = "Komprimering utan stöd"; -"tunnelkit.errors.vpn.network" = "Nätverk ändrat"; -"tunnelkit.errors.vpn.routing" = "Saknad routing"; -"tunnelkit.errors.vpn.gateway" = "Ingen gateway"; -"tunnelkit.errors.vpn.shutdown" = "Servern stängs av"; - -"tunnelkit.errors.parsing" = "Det gick inte att analysera den angivna konfigurationsfilen (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Konfigurationsfilen innehåller ett felaktigt val (%@)."; -"tunnelkit.errors.openvpn.required_option" = "Konfigurationsfilen saknar ett obligatoriskt val (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Konfigurationsfilen innehåller ett stöd som inte stöds (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Konfigurationsfilen är korrekt men innehåller ett eventuellt stöd som inte stöds (%@). \n\nConnectivity kan bryta beroende på serverinställningar."; -"tunnelkit.errors.openvpn.passphrase_required" = "Var god ange krypteringslösenfrasen."; -"tunnelkit.errors.openvpn.decryption" = "Konfigurationen innehåller en krypterad privat nyckel och den kan inte dekrypteras. Kontrollera din inmatade lösenfras."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "Under användning"; -"organizer.sections.tv.profiles_list.header.p1" = "Öppna Passepartout på en iOS- eller macOS-enhet och slå på reglaget för \"Apple TV\" i en profil för att få den att synas här."; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Inga profiler"; - -"organizer.alerts.reddit.message" = "Visste du att Passepartout har en subreddit? Prenumerera på uppdateringar eller diskutera problem, funktioner, nya plattformar eller vad du vill. \n\nDet är också ett bra sätt att visa dig bryr dig om detta projekt."; -"organizer.alerts.reddit.buttons.subscribe" = "Prenumerera nu!"; - -"organizer.alerts.remove_profile.message" = "Är det säkert att du vill ta bort profilen %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Ny profil"; -"add_profile.shared.views.existing.header" = "Befintliga profiler"; -"add_profile.shared.alerts.overwrite.message" = "En profil med samma namn finns redan. Vill du ersätta den?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Ange lösenfras"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Här hittar du några leverantörer med förinställda konfigurationsprofiler."; -"add_profile.provider.items.update_list" = "Uppdatera listan"; -"add_profile.provider.errors.no_default_server" = "Ingen server hittades."; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Välkommen till Passepartout! \n\nAnvänd arrangören för att lägga till en ny profil."; - -"profile.sections.vpn.footer" = "Anslutningen kommer att upprättas vid behov."; -"profile.sections.status.header" = "Koppling"; -"profile.sections.provider_infrastructure.footer" = "Senast uppdaterad på %@."; -"profile.sections.tv.footer.encryption" = "Profiler är krypterade och tillgängliggörs på din Apple TV via iCloud."; -"profile.sections.tv.footer.restricted.p1" = "Anslutningen bryts dock efter %d minuter."; -"profile.sections.tv.footer.restricted.p2" = "Köp för att slippa begränsningen."; -"profile.sections.vpn_survives_sleep.footer" = "Inaktivera för att förbättra batterianvändningen, på bekostnad av tillfälliga avmattningar på grund av återuppkoppling."; -"profile.sections.vpn_resolves_hostname.footer" = "Föredragna i de flesta nätverk och krävs i vissa IPv6-nätverk. Inaktivera var DNS blockeras eller för att påskynda förhandlingar när DNS är långsamt att svara."; -"profile.sections.feedback.header" = "Feedback"; -"profile.items.use_profile.caption" = "Använd den här profilen"; -"profile.items.vpn_service.caption" = "Aktiverad"; -"profile.items.vpn.turn_on.caption" = "Aktivera VPN"; -"profile.items.vpn.turn_off.caption" = "Inaktivera VPN"; -"profile.items.connection_status.caption" = "Status"; -"profile.items.data_count.caption" = "Utbyttad data"; -"profile.items.randomizes_server.caption" = "Välj server slumpmässigt"; -"profile.items.provider.refresh.caption" = "Uppdatera infrastruktur"; -"profile.items.category.caption" = "Kategori"; -"profile.items.only_shows_favorites.caption" = "Visa endast favoritplatser"; -"profile.items.vpn_survives_sleep.caption" = "Håll dig levande i sömnen"; -"profile.items.vpn_resolves_hostname.caption" = "Lösa server värdnamn"; -"profile.items.tv_sharing.caption.limited" = "Begränsat till %d minuter"; -"profile.items.expires_at.caption" = "Utgång"; - -"profile.alerts.rename.title" = "Byt namn på profil"; -"profile.alerts.reconnect_vpn.message" = "Vill du återansluta till VPN?"; -"profile.alerts.uninstall_vpn.message" = "Vill du verkligen radera VPN-konfigurationen från enhetens inställningar? Detta kan fixa några trasiga VPN tillstånd och påverkar inte dina leverantörs- och värdprofiler."; -"profile.alerts.test_connectivity.title" = "Anslutningar"; -"profile.alerts.test_connectivity.messages.success" = "Din enhet är ansluten till Internet!"; -"profile.alerts.test_connectivity.messages.failure" = "Din enhet har ingen Internetanslutning, var god granska dina profilparametrar."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Plats"; -"provider.location.sections.empty_favorites.footer" = "Dra åt vänster på en plats för att lägga till eller ta bort den från favoriter."; -"provider.location.actions.favorite" = "Favorit"; -"provider.location.actions.unfavorite" = "Inte favorit"; - -"provider.preset.title" = "Förinställt"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Konto"; -"account.sections.credentials.header" = "Referenser"; -"account.sections.registration.footer" = "Hämta ett konto på %@ webbplatsen."; -"account.items.authentication_method.persistent" = "Persistent"; -"account.items.authentication_method.interactive" = "Interaktiv"; -"account.items.username.caption" = "Användarnamn"; -"account.items.username.placeholder" = "användarnamn"; -"account.items.password.caption" = "Lösenord"; -"account.items.password.placeholder" = "hemlighet"; -"account.items.seed.caption" = "Sådd"; -"account.items.open_guide.caption" = "Visa dina uppgifter"; -"account.items.signup.caption" = "Registrera med %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Använd dina %@ webbplatsuppgifter."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Använd dina %@ service-referenser, som kan skilja sig från webbplatsens referenser."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis numeriskt (utan utrymmen)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis ditt e-postmeddelande."; -"account.sections.guidance.footer.infrastructure.pia" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis numeriskt med ett prefix för \" p \"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Hitta dina %@ credentials i avsnittet \" Konto> OpenVPN / IKEv2 Användarnamn \"på webbplatsen."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis ditt e-postmeddelande."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Använd dina %@ webbplatsuppgifter. Ditt användarnamn är vanligtvis ditt e-postmeddelande."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Hitta din %@ credentials i OpenVPN Config Generator på webbplatsen."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Peer"; -"endpoint.wireguard.items.preshared_key.caption" = "Tidigare delad nyckel"; -"endpoint.wireguard.items.allowed_ip.caption" = "Tillåtet IP"; - -"endpoint.advanced.title" = "Tekniska detaljer"; -"endpoint.advanced.openvpn.sections.pull.header" = "Dra från servern"; -"endpoint.advanced.openvpn.sections.communication.header" = "Communication"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Om du slutade med bruten anslutning efter att ha ändrat kommunikationsparametrarna trycker du på för att återgå till den ursprungliga konfigurationen."; -"endpoint.advanced.openvpn.sections.compression.header" = "Kompression"; -"endpoint.advanced.openvpn.sections.network.header" = "Nätverk"; -"endpoint.advanced.openvpn.sections.other.header" = "Other"; -"endpoint.advanced.openvpn.items.route.caption" = "Rutt"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Cipher"; -"endpoint.advanced.openvpn.items.digest.caption" = "Autentisering"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Inbäddad"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Inramning"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Algoritm"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Utan stöd"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Återställ konfiguration"; -"endpoint.advanced.openvpn.items.client.caption" = "Certifikat"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Nyckel"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Verified"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Ej verifierad"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Omslagning"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Autentisering"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Kryptering"; -"endpoint.advanced.openvpn.items.eku.caption" = "Förlängd verifering"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d seconds"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Omförhandling"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "efter %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Omställ slutpunkt på slumpmässigt sätt"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Välj värdnamn slumpmässigt"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Nätverksinställningar"; -"network_settings.sections.choices.header" = "Åsidosätt"; -"network_settings.gateway.title" = "Normal gateway"; -"network_settings.proxy.items.bypass_domains.caption" = "Kringgå domäner"; -"network_settings.items.add_dns_server.caption" = "Lägg till adress"; -"network_settings.items.add_dns_domain.caption" = "Lägg till domän"; -"network_settings.items.proxy_bypass.caption" = "Bypass-domän"; -"network_settings.items.add_proxy_bypass.caption" = "Add bypass domain"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "På begäran"; -"on_demand.sections.policy.footer" = "Aktivera VPN %@."; -"on_demand.items.add_ssid.caption" = "Lägg till Wi-Fi"; -"on_demand.items.active.caption" = "Betrodda"; -"on_demand.items.mobile.caption" = "Mobilt nätverk"; -"on_demand.items.ethernet.caption" = "Kabelanslutna uppkopplingar"; - -"on_demand.items.policy.caption" = "Förtroende inaktiverar VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Diagnostics"; -"diagnostics.sections.debug_log.footer" = "Masking status kommer att fungera efter återanslutning. Nätverksdata är värdnamn, IP-adresser, routing, SSID. Referenser och privata nycklar loggas inte oavsett."; -"diagnostics.items.server_configuration.caption" = "Server konfiguration"; -"diagnostics.items.app_log.title" = "App"; -"diagnostics.items.masks_private_data.caption" = "Mask nätverksdata"; -"diagnostics.items.report_issue.caption" = "Rapportera anslutningsproblem"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "För att säkert återställa den aktuella felsökningsloggen och tillämpa den nya maskeringspreferensen måste du återansluta till VPN nu."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Debug log"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Kopiera"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Rapportera problem"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Lägg till genväg"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Cellular"; -"shortcuts.add.items.connect.caption" = "Anslut till"; -"shortcuts.add.items.enable_vpn.caption" = "Aktivera VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Inaktivera VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Lita på nuvarande Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Avaktivera nuvarande Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Lita på mobilnätverk"; -"shortcuts.add.items.untrust_cellular.caption" = "Untrust cellular network"; -"shortcuts.add.alerts.no_profiles.message" = "Det finns ingen profil att ansluta till."; - -"shortcuts.edit.title" = "Hantera genvägar"; -"shortcuts.edit.sections.all.header" = "Befintliga genvägar"; -"shortcuts.edit.sections.add.footer" = "Få hjälp från Siri för att påskynda dina vanligaste interaktioner med appen."; -"shortcuts.edit.items.add_shortcut.caption" = "Lägg till genväg"; - -/* MARK: PaywallView */ - -"paywall.title" = "Köp"; -"paywall.sections.products.footer" = "Varje produkt är ett engångsköp. Leverantörsköp inkluderar inte ett VPN-abonnemang."; -"paywall.items.loading.caption" = "Laddar produkter"; -"paywall.items.full_version.extra_description" = "Alla leverantörer (inklusive framtida)\n%@"; -"paywall.items.restore.title" = "Återställ köp"; -"paywall.items.restore.description" = "Om du köpte den här appen eller funktionen tidigare kan du återställa dina inköp och den här skärmen visas inte igen."; -"paywall.alerts.purchase.appletv.success.message" = "Tack! Tidsbegränsningen kommer att försvinna så snart iCloud hinner ikapp. Vänta en liten stund och starta sedan om anslutningen i TV-appen."; - -/* MARK: DonateView */ - -"donate.title" = "Donera"; -"donate.sections.one_time.header" = "En gång"; -"donate.sections.one_time.footer" = "Om du vill visa tacksamhet för mitt fria arbete, här är några belopp du kan donera direkt. \n\nDu betalas endast en gång per donation, och du kan donera flera gånger. "; -"donate.items.loading.caption" = "Laddar donationer"; -"donate.items.purchasing.caption" = "Performing donation"; -"donate.alerts.purchase.success.message" = "Detta betyder mycket för mig och jag hoppas verkligen att du fortsätter att använda och marknadsföra denna app."; -"donate.alerts.purchase.failure.message" = "Kan inte göra donationen. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Inställningar"; -"settings.items.locks_in_background.caption" = "Åtkomst till låst app"; -"settings.items.donate.caption" = "Gör en donation"; - -/* MARK: AboutView */ - -"about.title" = "About"; -"about.sections.web.header" = "Web"; -"about.items.credits.caption" = "Credits"; -"about.items.join_community.caption" = "Gå med i communityn"; -"about.items.write_review.caption" = "Skriv en recension"; -"about.items.share_twitter.caption" = "Tweet om det!"; -"about.items.website.caption" = "Hemsida"; -"about.items.disclaimer.caption" = "Disclaimer"; -"about.items.privacy_policy.caption" = "Sekretesspolicy"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Version"; -"version.labels.intro" = "Passepartout och TunnelKit skrivs och underhålls av Davide De Rosa (keeshux). \n\nKällkod för Passepartout och TunnelKit är offentligt tillgänglig på GitHub under GPLv3, du kan hitta länkar på hemsidan."; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Credits"; -"credits.sections.licenses.header" = "Licenses"; -"credits.sections.notices.header" = "Meddelanden"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Inställningar"; -"preferences.sections.general.header" = "Allmänt"; -"preferences.items.launches_on_login.caption" = "Öppna vid inloggning"; -"preferences.items.launches_on_login.footer" = "Markera för att starta appen automatiskt efter omstart eller vid inloggning."; -"preferences.items.confirm_quit.caption" = "Bekräfta lämna"; -"preferences.items.confirm_quit.footer" = "Markera för att visa en uppmaning att bekräfta att man vill lämna appen."; diff --git a/Passepartout/App/uk.lproj/InfoPlist.strings b/Passepartout/App/uk.lproj/InfoPlist.strings deleted file mode 100644 index 6dd6c1d1..00000000 --- a/Passepartout/App/uk.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "Ім'я доступу до поточної мережі Wi-Fi"; - -"NSFaceIDUsageDescription" = "Розблокуйте додаток за допомогою Face ID"; diff --git a/Passepartout/App/uk.lproj/Localizable.strings b/Passepartout/App/uk.lproj/Localizable.strings deleted file mode 100644 index f6d040fc..00000000 --- a/Passepartout/App/uk.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "Політики"; -"global.strings.networks" = "Мережі"; -"global.strings.edit" = "Редагувати"; -"global.strings.unknown" = "Невідомо"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "Будь ласка, оберіть точку підключення"; - -"on_demand.sections.policy.footer.any" = "в будь якій мережі"; -"on_demand.sections.policy.footer.including" = "тільки в наступних мережах"; -"on_demand.sections.policy.footer.excluding" = "виключаючі наступні мережі"; -"on_demand.policy.any" = "Усі мережі"; -"on_demand.policy.including" = "Включити"; -"on_demand.policy.excluding" = "Виключити"; - -"settings.sections.icloud.footer" = "Виключіть синхронизацію щоб видалити. Для того шоб видалити дані з iCloud безпечно, зробіть це на усіх ваших пристроях. Це не вплине на локальні профілі."; -"settings.items.should_enable_cloud_syncing.caption" = "Синхронізація з iCloud"; -"settings.items.erase_cloud_store.caption" = "Видалити збереженне в iCloud"; - -/* MARK: Global */ - -"global.strings.cancel" = "Скасувати"; -"global.strings.next" = "Далі"; -/* MARK: Global */ -"global.strings.ok" = "Ок"; -"global.strings.save" = "Зберегти"; -"global.strings.rename" = "Змінити"; -"global.strings.duplicate" = "Дублювати"; -"global.strings.add" = "Додати"; -"global.strings.delete" = "Видалити"; -"global.strings.uninstall" = "Видалити застосунок"; -"global.strings.default" = "За замовчуванням"; -"global.strings.name" = "Ім'я"; -"global.strings.profiles" = "Профілі"; -"global.strings.provider" = "Провайдер"; -"global.strings.providers" = "Провайдери"; -"global.strings.configuration" = "Конфігурація"; -"global.strings.address" = "Адреса"; -"global.strings.addresses" = "Адреси"; -"global.strings.port" = "порт"; -"global.strings.protocol" = "Протокол"; -"global.strings.protocols" = "Протоколи"; -"global.strings.enabled" = "Увімкнено"; -"global.strings.disabled" = "Вімкнено"; -"global.strings.none" = "Немає"; -"global.strings.automatic" = "Автоматично"; -"global.strings.manual" = "Вручну"; -"global.strings.encryption" = "Шифрування"; -"global.strings.reconnect" = "Перепідключити"; -"global.strings.servers" = "Сервери"; -"global.strings.domain" = "Домен"; -"global.strings.domains" = "Домени"; -"global.strings.proxy" = "Проксі"; -"global.strings.bytes" = "байти"; -"global.strings.interface" = "Інтерфейс"; -"global.strings.private_key" = "Закритий ключ"; -"global.strings.public_key" = "Відкритий ключ"; -"global.strings.endpoint" = "Кінцева точка"; -"global.strings.keepalive" = "Підтримувати підключеним"; -"global.strings.advanced" = "Додадкові"; -"global.strings.translations" = "Переклади"; -"global.strings.show" = "Показати"; -"global.strings.connect" = "З'єднатися"; -"global.strings.disconnect" = "Від'єднатися"; -"global.strings.download" = "Завантажити"; -"global.strings.authentication" = "Автентифікація"; -"global.messages.unlock_app" = "Passepartout заблоковано"; -"global.messages.email_not_configured" = "Не налаштовано обліковий запис електронної пошти"; -"global.messages.share" = "Passepartout — це зручний у використанні клієнт OpenVPN / WireGuard для iOS и macOS з відкритим кодом"; - -"global.alerts.buttons.remind" = "Нагадати пізніше"; -"global.alerts.buttons.never" = "Більше не питати"; - -"global.placeholders.profile_name" = "Мій профіль"; - -"global.errors.missing_profile" = "Відсутній профіль"; -"global.errors.missing_account" = "Відсутній обліковий запис"; -"global.errors.missing_provider_server" = "Відсутнє розташування"; -"global.errors.missing_provider_preset" = "Відсутній пресет"; -"global.errors.tunnel_expired" = "Час на підключення закінчився"; - -/* MARK: Menus */ - -"menu.all.support.title" = "Підтримка"; -"menu.all.share.title" = "Поділитися"; -"menu.all.about.title" = "Про %@"; - -"menu.contextual.add_profile.from_files" = "З файлу"; -"menu.contextual.add_profile.from_text" = "З тексту"; -"menu.contextual.add_profile.imported" = "Додати %@"; -"menu.contextual.support.write_review" = "Відгук"; -"menu.contextual.support.join_community" = "Спільнота"; -"menu.contextual.share_twitter" = "Твіт"; -"menu.contextual.share_generic" = "Запросити"; - -"menu.system.quit.title" = "Вийти з %@"; - -"menu.system.quit.messages.confirm" = "---"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "Підключається"; -"tunnelkit.vpn.active" = "Активний"; -"tunnelkit.vpn.disconnecting" = "Відключається"; -"tunnelkit.vpn.inactive" = "Не активний"; -"tunnelkit.vpn.disabled" = "Вимкнений"; -"tunnelkit.vpn.unused" = "Вимк"; - -"tunnelkit.errors.vpn.timeout" = "Тайм-аут"; -"tunnelkit.errors.vpn.dns" = "Помилка DNS"; -"tunnelkit.errors.vpn.auth" = "Помилка автентифікації"; -"tunnelkit.errors.vpn.tls" = "Помилка TSL"; -"tunnelkit.errors.vpn.encryption" = "Помилка розшифрування"; -"tunnelkit.errors.vpn.compression" = "Компресія не підтримується"; -"tunnelkit.errors.vpn.network" = "Зміна мережі"; -"tunnelkit.errors.vpn.routing" = "Відсутня маршрутизація"; -"tunnelkit.errors.vpn.gateway" = "Немає шлюзу"; -"tunnelkit.errors.vpn.shutdown" = "Сервер вимкнено"; - -"tunnelkit.errors.parsing" = "Не вдається розібрати наданий файл конфігурації (%@)."; -"tunnelkit.errors.openvpn.malformed" = "Файл конфігурації містить некоректну опцію (%@)."; -"tunnelkit.errors.openvpn.required_option" = "У конфігураційному файлі відсутній обов’язковий параметр (%@)."; -"tunnelkit.errors.openvpn.unsupported_option" = "Конфігураційний файл містить параметр, який не підтримується (%@)."; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "Файл конфігурації вірний, але можливо містить параметр який не підтримується (%@).\n\nЗ'єднання може розірватися залежно від налаштувань сервера"; -"tunnelkit.errors.openvpn.passphrase_required" = "Будь ласка, введіть кодову фразу шифрування"; -"tunnelkit.errors.openvpn.decryption" = "Конфігурація містить зашифрований приватний ключ, він не може бути розшифрований. Перевірте кодову фразу."; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "Використовується"; -"organizer.sections.tv.profiles_list.header.p1" = "Відкрийте Passepartout на вашому iOS чи macOS пристрої та включіть перемикач \"Apple TV\" в профілі щоб він тут з'явився"; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "Немає профілів"; - -"organizer.alerts.reddit.message" = "Чи знали ви, що Passepartout має свій сабреддит? Підписуйтесь для отримання оновлень, обговорення проблем, функцій, нових платформ або будь-чого.\n\nЦе також чудовий спосіб показати підтримку проекту."; -"organizer.alerts.reddit.buttons.subscribe" = "Підпишіться зараз!"; - -"organizer.alerts.remove_profile.message" = "Ви впевнені, що хочете видалити профіль %@?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "Новий профіль"; -"add_profile.shared.views.existing.header" = "Існуючі профілі"; -"add_profile.shared.alerts.overwrite.message" = "Профіль з таким ім'ям вже існує. Замінити?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "Введіть кодову фразу"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "Тут ви знайдете декілька провайдерів з вже створеними профілями"; -"add_profile.provider.items.update_list" = "Оновити список"; -"add_profile.provider.errors.no_default_server" = "Не вдалося знайти сервер"; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "Ласкаво просимо до Passepartout!\n\nВикористовуйте органайзер, щоб додати новий профіль."; - -"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" = "Відгук"; -"profile.items.use_profile.caption" = "Використовувати цей профіль."; -"profile.items.vpn_service.caption" = "Увімкнено"; -"profile.items.vpn.turn_on.caption" = "Увімкнути VPN"; -"profile.items.vpn.turn_off.caption" = "Вимкнути VPN"; -"profile.items.connection_status.caption" = "Статус"; -"profile.items.data_count.caption" = "Обмін даними"; -"profile.items.randomizes_server.caption" = "Випадковий сервер"; -"profile.items.provider.refresh.caption" = "Оновити інфраструктуру"; -"profile.items.category.caption" = "Категорія"; -"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?"; -"profile.alerts.uninstall_vpn.message" = "Ви справді хочете стерти конфігурацію VPN із налаштувань пристрою? Це може виправити деякі VPN помилки, але не змінить налаштування застосунку."; -"profile.alerts.test_connectivity.title" = "Связь"; -"profile.alerts.test_connectivity.messages.success" = "Ваш пристрій підключено до Інтернету!"; -"profile.alerts.test_connectivity.messages.failure" = "Ваш пристрій не підключено до інтернету, будь ласка, перевірте налаштування вашого профілю."; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "Розташування"; -"provider.location.sections.empty_favorites.footer" = "Свайп в ліво на локації, щоб додати або видалити його з обраних."; -"provider.location.actions.favorite" = "Додати в обране"; -"provider.location.actions.unfavorite" = "Вилучити з обарного"; - -"provider.preset.title" = "Пресет"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "Акаунт"; -"account.sections.credentials.header" = "Дані для входу"; -"account.sections.registration.footer" = "Створіть обліковий запис на сторінці %@."; -"account.items.authentication_method.persistent" = "Наполегливий"; -"account.items.authentication_method.interactive" = "Інтерактивний"; -"account.items.username.caption" = "Логін"; -"account.items.username.placeholder" = "логін"; -"account.items.password.caption" = "Пароль"; -"account.items.password.placeholder" = "пароль"; -"account.items.seed.caption" = "насіння"; -"account.items.open_guide.caption" = "Перевірте ваші дані"; -"account.items.signup.caption" = "Зарееструйтесь з %@"; - -"account.sections.guidance.footer.infrastructure.default.web" = "Використовуйте свої облікові дані з веб-сайту %@."; -"account.sections.guidance.footer.infrastructure.default.specific" = "Використовуйте свої облікові дані %@ сервісу, які можуть відрізнятися від облікових даних веб-сайту."; -"account.sections.guidance.footer.infrastructure.mullvad" = "Використовуйте свої облікові дані з веб-сайту %@. Ваш логін зазвичай є числовим (без пробілів)."; -"account.sections.guidance.footer.infrastructure.nordvpn" = "Використовуйте дані для входу у %@ веб-сайт. Ваш логін зазвичай це ваш email."; -"account.sections.guidance.footer.infrastructure.pia" = "Використовуйте ваші дані з веб-сайту %@. Ваш логін зазвичай числовий з префіксом \"p\"."; -"account.sections.guidance.footer.infrastructure.protonvpn" = "Знайдіть ваші дані для входу %@ \"Account > OpenVPN / IKEv2 Username\" розділу веб-сайту."; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "Використовуйте дані для входу до %@ веб-сайту. Ваш логін зазвичай це ваш email."; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "Використовуйте дані для входу до %@ веб-сайту. Ваш логін зазвичай це ваш email."; -"account.sections.guidance.footer.infrastructure.windscribe" = "Знайдіть ваші дані для входу %@ у OpenVPN Config Generator на веб-сайті."; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "Вузол"; -"endpoint.wireguard.items.preshared_key.caption" = "Спільний ключ"; -"endpoint.wireguard.items.allowed_ip.caption" = "Дозволений IP"; - -"endpoint.advanced.title" = "Додаткові параметри"; -"endpoint.advanced.openvpn.sections.pull.header" = "Брати з сервера"; -"endpoint.advanced.openvpn.sections.communication.header" = "Зв'язок"; -"endpoint.advanced.openvpn.sections.reset.footer" = "Якщо після зміни параметрів зв’язку у вас виникло порушення з’єднання, натисніть, щоб повернутися до початковоі конфігурації."; -"endpoint.advanced.openvpn.sections.compression.header" = "Компресія"; -"endpoint.advanced.openvpn.sections.network.header" = "Мережа"; -"endpoint.advanced.openvpn.sections.other.header" = "Інше"; -"endpoint.advanced.openvpn.items.route.caption" = "Маршрут"; -"endpoint.advanced.openvpn.items.cipher.caption" = "Шифр"; -"endpoint.advanced.openvpn.items.digest.caption" = "Автентифікація"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "Вбудована"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "Фреймінг"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "Алгоритм"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "Не підтримується"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "Скинути конфігурацію"; -"endpoint.advanced.openvpn.items.client.caption" = "Сертифікат"; -"endpoint.advanced.openvpn.items.client_key.caption" = "Ключ"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "Перевірено"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "Не перевірено"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "Упаковуємо"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "Автентифікація"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "Шифрування"; -"endpoint.advanced.openvpn.items.eku.caption" = "Додаткова перевірка"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "кожні %d секунд"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "Переукладення"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "після %@"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "Випадкова кінцева точка"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "Обирати імена хостів випадковим чином"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "Параметри мережі"; -"network_settings.sections.choices.header" = "Ігнорувати"; -"network_settings.gateway.title" = "Шлюз за замовчуванням"; -"network_settings.proxy.items.bypass_domains.caption" = "Обхідні домени"; -"network_settings.items.add_dns_server.caption" = "Додати адресу"; -"network_settings.items.add_dns_domain.caption" = "Додати пошуковий домен"; -"network_settings.items.proxy_bypass.caption" = "Обхід домену"; -"network_settings.items.add_proxy_bypass.caption" = "Додати обхідний домен"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "Довірені мережі"; -"on_demand.sections.policy.footer" = "При підключені до довірених мереж VPN зазвичай вимикається та залишається відключеним. Вимкніть цю опцію щоб залишити VPN увімкненим."; -"on_demand.items.add_ssid.caption" = "Додати Wi-Fi"; -"on_demand.items.active.caption" = "Довірені"; -"on_demand.items.mobile.caption" = "Мобільна мережа"; -"on_demand.items.ethernet.caption" = "Довірені ethernet підключення"; - -"on_demand.items.policy.caption" = "Дов. мережа вимикає VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "Диагностика"; -"diagnostics.sections.debug_log.footer" = "Маскування увімкнеться після повторного підключення. Інформація про мережу - це назви хост профілів, IP адреса, маршрути та SSID. Дані для входу та приватні ключі не збираються."; -"diagnostics.items.server_configuration.caption" = "Конфігурація сервера"; -"diagnostics.items.app_log.title" = "Застосунок"; -"diagnostics.items.masks_private_data.caption" = "Маскувати інформацію про мережу"; -"diagnostics.items.report_issue.caption" = "Повідомити про проблему підкл."; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "Для безпечного скидання журналу налагодження та зміни маскування інформації мережі ви повинні знову підключитися до VPN."; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "Діагностичний журнал"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "Копіювати"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "Повідомити про проблему"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "Створити команду"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "Мобільна мережа"; -"shortcuts.add.items.connect.caption" = "Підключитися до"; -"shortcuts.add.items.enable_vpn.caption" = "Увімкнути VPN"; -"shortcuts.add.items.disable_vpn.caption" = "Відключити VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "Довіряти поточному Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "Не довіряти поточному Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "Довіряти мобільній мережі"; -"shortcuts.add.items.untrust_cellular.caption" = "Не довіряти мобільній мережі"; -"shortcuts.add.alerts.no_profiles.message" = "Немає профіля для підключення"; - -"shortcuts.edit.title" = "Керувати командами"; -"shortcuts.edit.sections.all.header" = "Існуючі команди"; -"shortcuts.edit.sections.add.footer" = "Отримати допомогу від Siri, щоб пришвидшити найчастіші дії з програмою."; -"shortcuts.edit.items.add_shortcut.caption" = "Створити команду"; - -/* MARK: PaywallView */ - -"paywall.title" = "Придбати"; -"paywall.sections.products.footer" = "Кожен товар є одноразовою покупкою. Покупки провайдерів не включають підписку на VPN"; -"paywall.items.loading.caption" = "Завантаженя товарів"; -"paywall.items.full_version.extra_description" = "Всі провайдери (в тому числі майбутні)\n%@"; -"paywall.items.restore.title" = "Відновити покупки"; -"paywall.items.restore.description" = "Якщо ви придбали цю програму чи функцію раніше, ви можете відновити покупки, і цей екран більше не відображатиметься"; -"paywall.alerts.purchase.appletv.success.message" = "Дякую! Ліміти по часу буде прибрано, щойно синхронізується iCloud. Зачекайте кілька секунд, а потім перезапустіть з’єднання в програмі на AppleTV."; - -/* MARK: DonateView */ - -"donate.title" = "Зробити пожертвування"; -"donate.sections.one_time.header" = "Одноразово"; -"donate.sections.one_time.footer" = "Якщо ви бажаєте подякувати за мою безкоштовну роботу, тут є декілька сум, які ви можете пожертвувати прямо зараз.\n\nЗ вас буде стягнено плату лише один раз, але ви можете робити пожертви кілька разів."; -"donate.items.loading.caption" = "Завантажуємо пожертвування"; -"donate.items.purchasing.caption" = "Виконується"; -"donate.alerts.purchase.success.message" = "Це дуже важливо для мене, і я сподіваюся, що ви й надалі будете користуватися цією програмою та рекламувати її."; -"donate.alerts.purchase.failure.message" = "Не вдається здійснити пожертвування. %@"; - -/* MARK: SettingsView */ - -"settings.title" = "Налаштування"; -"settings.items.locks_in_background.caption" = "Заблокувати доступ до програми"; -"settings.items.donate.caption" = "Зробити пожертвування"; - -/* MARK: AboutView */ - -"about.title" = "Про нас"; -"about.sections.web.header" = "Веб"; -"about.items.credits.caption" = "Подяки"; -"about.items.join_community.caption" = "Вступити до спільноти"; -"about.items.write_review.caption" = "Залишити відгук"; -"about.items.share_twitter.caption" = "Твітніть про нас!"; -"about.items.website.caption" = "Домашня сторінка"; -"about.items.disclaimer.caption" = "Відмова від відповідальності"; -"about.items.privacy_policy.caption" = "Політика конфіденційності"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "Версія"; -"version.labels.intro" = "Passepartout та TunnelKit створені та підтримуются Davide De Rosa (keeshux).\n\nПочаткові коди для Passepartout и TunnelKit публічно доступні на GitHub під GPLv3, ви можете знайти посилання на домашній сторінці"; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "Подяка"; -"credits.sections.licenses.header" = "Ліцензії"; -"credits.sections.notices.header" = "Примітки"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "Налаштування"; -"preferences.sections.general.header" = "Загальні"; -"preferences.items.launches_on_login.caption" = "Запускати під час входу в систему"; -"preferences.items.launches_on_login.footer" = "Увімкніть, щоб програма автоматично запускалася під час завантаження або входу."; -"preferences.items.confirm_quit.caption" = "Підтвердити вихід"; -"preferences.items.confirm_quit.footer" = "Увімкніть, щоб вихід треба було підтверджувати."; diff --git a/Passepartout/App/zh-Hans.lproj/InfoPlist.strings b/Passepartout/App/zh-Hans.lproj/InfoPlist.strings deleted file mode 100644 index 6c9f5444..00000000 --- a/Passepartout/App/zh-Hans.lproj/InfoPlist.strings +++ /dev/null @@ -1,28 +0,0 @@ -// -// InfoPlist.strings -// Passepartout -// -// Created by Davide De Rosa on 10/23/19. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -"NSLocationWhenInUseUsageDescription" = "当前Wi-Fi的访问名称"; - -"NSFaceIDUsageDescription" = "用面容ID解锁应用"; diff --git a/Passepartout/App/zh-Hans.lproj/Localizable.strings b/Passepartout/App/zh-Hans.lproj/Localizable.strings deleted file mode 100644 index 1ec10696..00000000 --- a/Passepartout/App/zh-Hans.lproj/Localizable.strings +++ /dev/null @@ -1,381 +0,0 @@ -// -// Localizable.strings -// Passepartout -// -// Created by Davide De Rosa on 6/13/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -"global.strings.policy" = "政策"; -"global.strings.networks" = "网络"; -"global.strings.edit" = "编辑"; -"global.strings.unknown" = "未知"; -/* MARK: ProfileView -> EndpointView */ - -"endpoint.errors.endpoint_required" = "请选择一个终端"; - -"on_demand.sections.policy.footer.any" = "在任何网络中"; -"on_demand.sections.policy.footer.including" = "仅在以下网络中"; -"on_demand.sections.policy.footer.excluding" = "以下网络除外"; -"on_demand.policy.any" = "全部网络"; -"on_demand.policy.including" = "包括"; -"on_demand.policy.excluding" = "排除"; - -"settings.sections.icloud.footer" = "禁用同步以允许移除。若要安全地移除iCloud存储,请在所有同步设备上执行此操作。这不会影响本地配置文件。"; -"settings.items.should_enable_cloud_syncing.caption" = "与iCloud同步"; -"settings.items.erase_cloud_store.caption" = "移除iCloud储存"; - -/* MARK: Global */ - -"global.strings.cancel" = "取消"; -"global.strings.next" = "下一步"; -/* MARK: Global */ -"global.strings.ok" = "好的"; -"global.strings.save" = "保存"; -"global.strings.rename" = "重命名"; -"global.strings.duplicate" = "复制"; -"global.strings.add" = "添加"; -"global.strings.delete" = "删除"; -"global.strings.uninstall" = "卸载"; -"global.strings.default" = "默认"; -"global.strings.name" = "名称"; -"global.strings.profiles" = "配置"; -"global.strings.provider" = "提供商"; -"global.strings.providers" = "提供商"; -"global.strings.configuration" = "配置"; -"global.strings.address" = "地址"; -"global.strings.addresses" = "地址"; -"global.strings.port" = "端口"; -"global.strings.protocol" = "协议"; -"global.strings.protocols" = "协议"; -"global.strings.enabled" = "已启用"; -"global.strings.disabled" = "未启用"; -"global.strings.none" = "无"; -"global.strings.automatic" = "自动设置"; -"global.strings.manual" = "手动设置"; -"global.strings.encryption" = "加密"; -"global.strings.reconnect" = "重连"; -"global.strings.servers" = "服务器"; -"global.strings.domain" = "域名"; -"global.strings.domains" = "域"; -"global.strings.proxy" = "代理"; -"global.strings.bytes" = "Bytes"; -"global.strings.interface" = "界面"; -"global.strings.private_key" = "密钥"; -"global.strings.public_key" = "公共密钥"; -"global.strings.endpoint" = "终端"; -"global.strings.keepalive" = "保持活跃状态"; -"global.strings.advanced" = "高级"; -"global.strings.translations" = "翻译"; -"global.strings.show" = "显示"; -"global.strings.connect" = "连接"; -"global.strings.disconnect" = "断开连接"; -"global.strings.download" = "下载"; -"global.strings.authentication" = "身份验证"; -"global.messages.unlock_app" = "Passepartout已被锁定"; -"global.messages.email_not_configured" = "未配置e-mail账户。"; -"global.messages.share" = "Passepartout是适用于iOS和macOS操作系统的OpenVPN/WireGuard开源客户端,易于使用"; // FIXME: l10n, Apple platforms - -"global.alerts.buttons.remind" = "稍后提醒我"; -"global.alerts.buttons.never" = "不要再问"; - -"global.placeholders.profile_name" = "我的配置"; - -"global.errors.missing_profile" = "缺少配置"; -"global.errors.missing_account" = "缺少帐户"; -"global.errors.missing_provider_server" = "缺少位置"; -"global.errors.missing_provider_preset" = "缺少预设"; -"global.errors.tunnel_expired" = "连接已过期"; - -/* MARK: Menus */ - -"menu.all.support.title" = "支持"; -"menu.all.share.title" = "分享"; -"menu.all.about.title" = "关于 %@"; - -"menu.contextual.add_profile.from_files" = "从文件"; -"menu.contextual.add_profile.from_text" = "从文本"; -"menu.contextual.add_profile.imported" = "添加%@"; -"menu.contextual.support.write_review" = "评价"; -"menu.contextual.support.join_community" = "社区"; -"menu.contextual.share_twitter" = "发推文"; -"menu.contextual.share_generic" = "邀请"; - -"menu.system.quit.title" = "退出%@"; - -"menu.system.quit.messages.confirm" = "VPN(如果启用)仍将在后台运行。您要退出吗?"; -/* MARK: TunnelKit */ - -"tunnelkit.vpn.connecting" = "连接中"; -"tunnelkit.vpn.active" = "活动的"; -"tunnelkit.vpn.disconnecting" = "断开中"; -"tunnelkit.vpn.inactive" = "不活动"; -"tunnelkit.vpn.disabled" = "未启用"; -"tunnelkit.vpn.unused" = "关"; - -"tunnelkit.errors.vpn.timeout" = "超时"; -"tunnelkit.errors.vpn.dns" = "DNS解析失败"; -"tunnelkit.errors.vpn.auth" = "身份验证失败"; -"tunnelkit.errors.vpn.tls" = "TLS连接失败"; -"tunnelkit.errors.vpn.encryption" = "加密失败"; -"tunnelkit.errors.vpn.compression" = "压缩方式不支持"; -"tunnelkit.errors.vpn.network" = "网络更改"; -"tunnelkit.errors.vpn.routing" = "缺少路由"; -"tunnelkit.errors.vpn.gateway" = "无网关"; -"tunnelkit.errors.vpn.shutdown" = "服务器关闭"; - -"tunnelkit.errors.parsing" = "无法解析提供的配置文件(%@)。"; -"tunnelkit.errors.openvpn.malformed" = "此配置文件包含格式错误的选项(%@)。"; -"tunnelkit.errors.openvpn.required_option" = "此配置文件缺少必须的选项(%@)。"; -"tunnelkit.errors.openvpn.unsupported_option" = "此配置文件包含不支持的选项(%@)。"; -"tunnelkit.errors.openvpn.potentially_unsupported_option" = "配置文件正确但包含不支持的选项(%@)。\n\n根据服务端的设置连接可能会断开。"; -"tunnelkit.errors.openvpn.passphrase_required" = "请输入加密密码"; -"tunnelkit.errors.openvpn.decryption" = "该配置包含加密私钥且不能被解密。请再次检查输入的密钥。"; - -/* MARK: OrganizerView */ - -"organizer.sections.active" = "使用中"; -"organizer.sections.tv.profiles_list.header.p1" = "在iOS或macOS设备上打开Passepartout,并启用个人资料下的“Apple TV”,使其显示在此处。"; -/* MARK: OrganizerView */ -"organizer.empty.no_profiles" = "没有配置"; - -"organizer.alerts.reddit.message" = "你知道Passepartout有一个subreddit吗?可以在上面讨论更新、问题、功能、新的平台等n任何你想要的。\n\n这同样是表达你对此项目关注的地方。"; -"organizer.alerts.reddit.buttons.subscribe" = "立即订阅!"; - -"organizer.alerts.remove_profile.message" = "确定要删除配置%@吗?"; - -/* MARK: AddProfileView */ - -"add_profile.shared.title" = "新的配置"; -"add_profile.shared.views.existing.header" = "已存在的配置"; -"add_profile.shared.alerts.overwrite.message" = "已经存在同名的配置。要替换吗?"; - -/* MARK: AddHostView */ - -"add_profile.host.sections.encryption.footer" = "输入密码"; - -/* MARK: AddProviderView */ - -"add_profile.provider.sections.vpn.footer" = "在这里你可以找到预设的新的提供商配置。"; -"add_profile.provider.items.update_list" = "更新列表"; -"add_profile.provider.errors.no_default_server" = "未找到任何服务器"; - -/* MARK: ProfileView */ - -"profile.welcome.message" = "欢迎使用Passepartout!\n\n使用分类页面来添加一个新的配置。"; - -"profile.sections.vpn.footer" = "必要时连接将会建立。"; -"profile.sections.status.header" = "连接"; -"profile.sections.provider_infrastructure.footer" = "最后在%@时更新"; -"profile.sections.tv.footer.encryption" = "用户资料经过加密,并通过iCloud共享至Apple TV。"; -"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被阻断或相应缓慢时禁用。"; -"profile.sections.feedback.header" = "反馈"; -"profile.items.use_profile.caption" = "使用此配置"; -"profile.items.vpn_service.caption" = "已启用"; -"profile.items.vpn.turn_on.caption" = "启用VPN"; -"profile.items.vpn.turn_off.caption" = "禁用VPN"; -"profile.items.connection_status.caption" = "状态"; -"profile.items.data_count.caption" = "已交换数据量"; -"profile.items.randomizes_server.caption" = "随机服务器"; -"profile.items.provider.refresh.caption" = "刷新基础设置"; -"profile.items.category.caption" = "类别"; -"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吗?"; -"profile.alerts.uninstall_vpn.message" = "你确定要从此设备移除VPN配置吗?这可能会固定一些断开的VPN状态,不会影响已经存在的配置。"; -"profile.alerts.test_connectivity.title" = "连接性"; -"profile.alerts.test_connectivity.messages.success" = "你的设备连接到了网络!"; -"profile.alerts.test_connectivity.messages.failure" = "你的设备没有连接到网络,请检查你的配置参数。"; - -/* MARK: ProfileView -> Provider*View */ - -"provider.location.title" = "位置"; -"provider.location.sections.empty_favorites.footer" = "向左轻扫以将其从最喜爱列表中移除或添加。"; -"provider.location.actions.favorite" = "最喜爱"; -"provider.location.actions.unfavorite" = "不喜爱"; - -"provider.preset.title" = "预设"; - -/* MARK: ProfileView -> AccountView */ - -"account.title" = "账户"; -"account.sections.credentials.header" = "认证方式"; -"account.sections.registration.footer" = "在%@网站上获取一个账户"; -"account.items.authentication_method.persistent" = "持续"; -"account.items.authentication_method.interactive" = "互动"; -"account.items.username.caption" = "用户名"; -"account.items.username.placeholder" = "username"; -"account.items.password.caption" = "密码"; -"account.items.password.placeholder" = "secret"; -"account.items.seed.caption" = "种子"; -"account.items.open_guide.caption" = "查看你的认证信息"; -"account.items.signup.caption" = "以%@注册"; - -"account.sections.guidance.footer.infrastructure.default.web" = "使用你的%@网站认证信息。"; -"account.sections.guidance.footer.infrastructure.default.specific" = "使用您的%@服务凭据,该凭据可能与网站凭据不同。"; -"account.sections.guidance.footer.infrastructure.mullvad" = "使用你的%@网站认证信息。你的用户名一般是数字 (没有空格)。"; -"account.sections.guidance.footer.infrastructure.nordvpn" = "使用你的%@网站认证信息。你的用户名一般是你的e-mail。"; -"account.sections.guidance.footer.infrastructure.pia" = "使用你的%@网站认证信息。你的用户名一般是数字且带有前缀\"p\"。"; -"account.sections.guidance.footer.infrastructure.protonvpn" = "在网站的\"Account > OpenVPN / IKEv2 Username\"部分找到你的%@的认证信息。"; -"account.sections.guidance.footer.infrastructure.tunnelbear" = "使用你的%@网站认证信息。你的用户名一般是你的e-mail。"; -"account.sections.guidance.footer.infrastructure.vyprvpn" = "使用你的%@网站认证信息。你的用户名一般是你的e-mail。"; -"account.sections.guidance.footer.infrastructure.windscribe" = "在网站上的OpenVPN配置生成器中找到你的%@认证信息。 "; - -/* MARK: ProfileView -> EndpointView */ -"endpoint.wireguard.items.peer.caption" = "同级"; -"endpoint.wireguard.items.preshared_key.caption" = "预共用密码"; -"endpoint.wireguard.items.allowed_ip.caption" = "允许的IP"; - -"endpoint.advanced.title" = "技术细节"; -"endpoint.advanced.openvpn.sections.pull.header" = "从服务器拉取"; -"endpoint.advanced.openvpn.sections.communication.header" = "通信"; -"endpoint.advanced.openvpn.sections.reset.footer" = "如果你在更改连接参数后变成断开状态,点按已恢复到原始的配置。"; -"endpoint.advanced.openvpn.sections.compression.header" = "压缩"; -"endpoint.advanced.openvpn.sections.network.header" = "网络"; -"endpoint.advanced.openvpn.sections.other.header" = "其他"; -"endpoint.advanced.openvpn.items.route.caption" = "路由"; -"endpoint.advanced.openvpn.items.cipher.caption" = "加密"; -"endpoint.advanced.openvpn.items.digest.caption" = "身份验证"; -"endpoint.advanced.openvpn.items.digest.value.embedded" = "已包含"; -"endpoint.advanced.openvpn.items.compression_framing.caption" = "分帧"; -"endpoint.advanced.openvpn.items.compression_algorithm.caption" = "算法"; -"endpoint.advanced.openvpn.items.compression_algorithm.value.other" = "未支持"; -"endpoint.advanced.openvpn.items.reset_original.caption" = "重置配置"; -"endpoint.advanced.openvpn.items.client.caption" = "证书"; -"endpoint.advanced.openvpn.items.client_key.caption" = "密钥"; -"endpoint.advanced.openvpn.items.client.value.enabled" = "已验证"; -"endpoint.advanced.openvpn.items.client.value.disabled" = "未验证"; -"endpoint.advanced.openvpn.items.tls_wrapping.caption" = "组包"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.auth" = "身份验证"; -"endpoint.advanced.openvpn.items.tls_wrapping.value.crypt" = "加密"; -"endpoint.advanced.openvpn.items.eku.caption" = "扩展身份验证"; -"endpoint.advanced.openvpn.items.keep_alive.value.seconds" = "%d秒"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.caption" = "重协商"; -"endpoint.advanced.openvpn.items.renegotiation_seconds.value.after" = "在%@之后"; -"endpoint.advanced.openvpn.items.random_endpoint.caption" = "随机的服务端"; -"endpoint.advanced.openvpn.items.random_hostname.caption" = "随机主机名"; - -/* MARK: ProfileView -> NetworkSettingsView */ - -"network_settings.title" = "网络设置"; -"network_settings.sections.choices.header" = "覆盖"; -"network_settings.gateway.title" = "默认网关"; -"network_settings.proxy.items.bypass_domains.caption" = "旁路域"; -"network_settings.items.add_dns_server.caption" = "添加地址"; -"network_settings.items.add_dns_domain.caption" = "添加搜索域名"; -"network_settings.items.proxy_bypass.caption" = "旁路域名"; -"network_settings.items.add_proxy_bypass.caption" = "添加旁路域名"; - -/* MARK: ProfileView -> OnDemandView */ - -"on_demand.title" = "随需"; -"on_demand.sections.policy.footer" = "激活VPN %@。"; -"on_demand.items.add_ssid.caption" = "新增Wi-Fi"; -"on_demand.items.active.caption" = "信任"; -"on_demand.items.mobile.caption" = "蜂窝网络"; -"on_demand.items.ethernet.caption" = "有线连接"; - -"on_demand.items.policy.caption" = "信任网络中禁用VPN"; -/* MARK: ProfileView -> DiagnosticsView */ - -"diagnostics.title" = "分析数据"; -"diagnostics.sections.debug_log.footer" = "在重连后状态隐藏才有效。网络数据包括主机名、IP地址、路由、SSID、认证方式,但私钥不会出现在日志中。"; -"diagnostics.items.server_configuration.caption" = "服务端配置"; -"diagnostics.items.app_log.title" = "应用程序"; -"diagnostics.items.masks_private_data.caption" = "隐藏网络数据"; -"diagnostics.items.report_issue.caption" = "报告连接问题"; - -"diagnostics.alerts.masks_private_data.messages.must_reconnect" = "为了安全地重置当前调试日志并应用新的掩码设置,你现在必须重连VPN。"; - -/* MARK: DiagnosticsView -> DebugLogView */ - -"debug_log.title" = "调试日志"; -/* MARK: DiagnosticsView -> DebugLogView */ -"debug_log.buttons.copy" = "复制"; - -/* MARK: DiagnosticsView -> ReportIssueView */ - -"report_issue.alert.title" = "提交问题"; - -/* MARK: ShortcutsView */ - -"shortcuts.add.title" = "添加捷径"; -"shortcuts.add.sections.wifi.header" = "Wi-Fi"; -"shortcuts.add.sections.cellular.header" = "蜂窝网络"; -"shortcuts.add.items.connect.caption" = "连接到"; -"shortcuts.add.items.enable_vpn.caption" = "开启VPN"; -"shortcuts.add.items.disable_vpn.caption" = "关闭VPN"; -"shortcuts.add.items.trust_current_wifi.caption" = "信任当前Wi-Fi"; -"shortcuts.add.items.untrust_current_wifi.caption" = "不信任当前Wi-Fi"; -"shortcuts.add.items.trust_cellular.caption" = "信任蜂窝网络"; -"shortcuts.add.items.untrust_cellular.caption" = "不信任蜂窝网络"; -"shortcuts.add.alerts.no_profiles.message" = "没有可以连接的配置。"; - -"shortcuts.edit.title" = "管理捷径"; -"shortcuts.edit.sections.all.header" = "已经存在的捷径"; -"shortcuts.edit.sections.add.footer" = "通过Siri来帮助你加快常用的操作。"; -"shortcuts.edit.items.add_shortcut.caption" = "添加捷径"; - -/* MARK: PaywallView */ - -"paywall.title" = "购买"; -"paywall.sections.products.footer" = "每件产品都是一次性的购买。 购买的提供商并不包含VPN订阅。"; -"paywall.items.loading.caption" = "加载产品中"; -"paywall.items.full_version.extra_description" = "所有的提供商(包括未来添加的)\n%@"; -"paywall.items.restore.title" = "恢复购买"; -"paywall.items.restore.description" = "如果你购买过此应用或其特征, 你可以恢复购买,此页面将不在显示。"; -"paywall.alerts.purchase.appletv.success.message" = "非常感谢!iCloud同步后,时间限制将立即取消。请稍等片刻,然后重新启动Apple TV应用程序的连接。"; - -/* MARK: DonateView */ - -"donate.title" = "捐助"; -"donate.sections.one_time.header" = "一次性"; -"donate.sections.one_time.footer" = "如果你想对我免费的工作表示感谢,这里是一些你可以捐助的数额。\n\n每次捐助只需要付款一次,你可以捐助多次。"; -"donate.items.loading.caption" = "加载捐助中"; -"donate.items.purchasing.caption" = "展示捐助页面中"; -"donate.alerts.purchase.success.message" = "这对于我意味着很多,希望你保持使用并使它更好。"; -"donate.alerts.purchase.failure.message" = "无法展现捐助内容。%@"; - -/* MARK: SettingsView */ - -"settings.title" = "设置"; -"settings.items.locks_in_background.caption" = "锁定应用访问权限"; -"settings.items.donate.caption" = "提供捐助"; - -/* MARK: AboutView */ - -"about.title" = "关于"; -"about.sections.web.header" = "网络"; -"about.items.credits.caption" = "评分"; -"about.items.join_community.caption" = "加入社区"; -"about.items.write_review.caption" = "写下想法"; -"about.items.share_twitter.caption" = "发送关于它的推特!"; -"about.items.website.caption" = "主页"; -"about.items.disclaimer.caption" = "免责声明"; -"about.items.privacy_policy.caption" = "隐私政策"; - -/* MARK: AboutView -> VersionView */ - -"version.title" = "版本"; -"version.labels.intro" = "Passepartout和TunnelKit由Davide De Rosa (keeshux)编写并维护。\n\nPassepartout和TunnelKit的源代码在GitHub上以GPLv3许可证面向大众开放,你可以在主页上找到链接。"; - -/* MARK: AboutView -> CreditsView */ - -"credits.title" = "评分"; -"credits.sections.licenses.header" = "许可证"; -"credits.sections.notices.header" = "注意"; - -/* MARK: PreferencesView (macOS) */ - -"preferences.title" = "偏好设置"; -"preferences.sections.general.header" = "一般"; -"preferences.items.launches_on_login.caption" = "登录时启动"; -"preferences.items.launches_on_login.footer" = "选中以在启动或登录时自动启动应用。"; -"preferences.items.confirm_quit.caption" = "确认退出"; -"preferences.items.confirm_quit.footer" = "选中以显示退出确认提醒。"; diff --git a/Passepartout/AppShared/Constants/SwiftGen+Strings.swift b/Passepartout/AppShared/Constants/SwiftGen+Strings.swift deleted file mode 100644 index acc7e951..00000000 --- a/Passepartout/AppShared/Constants/SwiftGen+Strings.swift +++ /dev/null @@ -1,1155 +0,0 @@ -// swiftlint:disable all -// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen - -import Foundation - -// swiftlint:disable superfluous_disable_command file_length implicit_return prefer_self_in_static_references - -// MARK: - Strings - -// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length -// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces -internal enum L10n { - internal enum About { - /// MARK: AboutView - internal static let title = L10n.tr("Localizable", "about.title", fallback: "About") - internal enum Items { - internal enum Credits { - /// Credits - internal static let caption = L10n.tr("Localizable", "about.items.credits.caption", fallback: "Credits") - } - internal enum Disclaimer { - /// Disclaimer - internal static let caption = L10n.tr("Localizable", "about.items.disclaimer.caption", fallback: "Disclaimer") - } - internal enum JoinCommunity { - /// Join community - internal static let caption = L10n.tr("Localizable", "about.items.join_community.caption", fallback: "Join community") - } - internal enum PrivacyPolicy { - /// Privacy policy - internal static let caption = L10n.tr("Localizable", "about.items.privacy_policy.caption", fallback: "Privacy policy") - } - internal enum ShareTwitter { - /// Tweet about it! - internal static let caption = L10n.tr("Localizable", "about.items.share_twitter.caption", fallback: "Tweet about it!") - } - internal enum Website { - /// Home page - internal static let caption = L10n.tr("Localizable", "about.items.website.caption", fallback: "Home page") - } - internal enum WriteReview { - /// Write a review - internal static let caption = L10n.tr("Localizable", "about.items.write_review.caption", fallback: "Write a review") - } - } - internal enum Sections { - internal enum Web { - /// Web - internal static let header = L10n.tr("Localizable", "about.sections.web.header", fallback: "Web") - } - } - } - internal enum Account { - /// MARK: ProfileView -> AccountView - internal static let title = L10n.tr("Localizable", "account.title", fallback: "Account") - internal enum Items { - internal enum AuthenticationMethod { - /// Interactive - internal static let interactive = L10n.tr("Localizable", "account.items.authentication_method.interactive", fallback: "Interactive") - /// Persistent - internal static let persistent = L10n.tr("Localizable", "account.items.authentication_method.persistent", fallback: "Persistent") - } - internal enum OpenGuide { - /// See your credentials - internal static let caption = L10n.tr("Localizable", "account.items.open_guide.caption", fallback: "See your credentials") - } - internal enum Password { - /// Password - internal static let caption = L10n.tr("Localizable", "account.items.password.caption", fallback: "Password") - /// secret - internal static let placeholder = L10n.tr("Localizable", "account.items.password.placeholder", fallback: "secret") - } - internal enum Seed { - /// Seed - internal static let caption = L10n.tr("Localizable", "account.items.seed.caption", fallback: "Seed") - } - internal enum Signup { - /// Register with %@ - internal static func caption(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.items.signup.caption", String(describing: p1), fallback: "Register with %@") - } - } - internal enum Username { - /// Username - internal static let caption = L10n.tr("Localizable", "account.items.username.caption", fallback: "Username") - /// username - internal static let placeholder = L10n.tr("Localizable", "account.items.username.placeholder", fallback: "username") - } - } - internal enum Sections { - internal enum Credentials { - /// Credentials - internal static let header = L10n.tr("Localizable", "account.sections.credentials.header", fallback: "Credentials") - } - internal enum Guidance { - internal enum Footer { - internal enum Infrastructure { - /// Use your %@ website credentials. Your username is usually numeric (without spaces). - internal static func mullvad(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.mullvad", String(describing: p1), fallback: "Use your %@ website credentials. Your username is usually numeric (without spaces).") - } - /// Use your %@ website credentials. Your username is usually your e-mail. - internal static func nordvpn(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.nordvpn", String(describing: p1), fallback: "Use your %@ website credentials. Your username is usually your e-mail.") - } - /// Use your %@ website credentials. Your username is usually numeric with a "p" prefix. - internal static func pia(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.pia", String(describing: p1), fallback: "Use your %@ website credentials. Your username is usually numeric with a \"p\" prefix.") - } - /// Find your %@ credentials in the "Account > OpenVPN / IKEv2 Username" section of the website. - internal static func protonvpn(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.protonvpn", String(describing: p1), fallback: "Find your %@ credentials in the \"Account > OpenVPN / IKEv2 Username\" section of the website.") - } - /// Use your %@ website credentials. Your username is usually your e-mail. - internal static func tunnelbear(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.tunnelbear", String(describing: p1), fallback: "Use your %@ website credentials. Your username is usually your e-mail.") - } - /// Use your %@ website credentials. Your username is usually your e-mail. - internal static func vyprvpn(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.vyprvpn", String(describing: p1), fallback: "Use your %@ website credentials. Your username is usually your e-mail.") - } - /// Find your %@ credentials in the OpenVPN Config Generator on the website. - internal static func windscribe(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.windscribe", String(describing: p1), fallback: "Find your %@ credentials in the OpenVPN Config Generator on the website.") - } - internal enum Default { - /// Use your %@ service credentials, which may differ from website credentials. - internal static func specific(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.default.specific", String(describing: p1), fallback: "Use your %@ service credentials, which may differ from website credentials.") - } - /// Use your %@ website credentials. - internal static func web(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.guidance.footer.infrastructure.default.web", String(describing: p1), fallback: "Use your %@ website credentials.") - } - } - } - } - } - internal enum Registration { - /// Go get an account on the %@ website. - internal static func footer(_ p1: Any) -> String { - return L10n.tr("Localizable", "account.sections.registration.footer", String(describing: p1), fallback: "Go get an account on the %@ website.") - } - } - } - } - internal enum AddProfile { - internal enum Host { - internal enum Sections { - internal enum Encryption { - /// MARK: AddHostView - internal static let footer = L10n.tr("Localizable", "add_profile.host.sections.encryption.footer", fallback: "Enter passphrase") - } - } - } - internal enum Provider { - internal enum Errors { - /// Could not find any server. - internal static let noDefaultServer = L10n.tr("Localizable", "add_profile.provider.errors.no_default_server", fallback: "Could not find any server.") - } - internal enum Items { - /// Update list - internal static let updateList = L10n.tr("Localizable", "add_profile.provider.items.update_list", fallback: "Update list") - } - internal enum Sections { - internal enum Vpn { - /// MARK: AddProviderView - internal static let footer = L10n.tr("Localizable", "add_profile.provider.sections.vpn.footer", fallback: "Here you find a few providers with preset configuration profiles.") - } - } - } - internal enum Shared { - /// MARK: AddProfileView - internal static let title = L10n.tr("Localizable", "add_profile.shared.title", fallback: "New profile") - internal enum Alerts { - internal enum Overwrite { - /// A profile with the same name already exists. Replace it? - internal static let message = L10n.tr("Localizable", "add_profile.shared.alerts.overwrite.message", fallback: "A profile with the same name already exists. Replace it?") - } - } - internal enum Views { - internal enum Existing { - /// Existing profiles - internal static let header = L10n.tr("Localizable", "add_profile.shared.views.existing.header", fallback: "Existing profiles") - } - } - } - } - internal enum Credits { - /// MARK: AboutView -> CreditsView - internal static let title = L10n.tr("Localizable", "credits.title", fallback: "Credits") - internal enum Sections { - internal enum Licenses { - /// Licenses - internal static let header = L10n.tr("Localizable", "credits.sections.licenses.header", fallback: "Licenses") - } - internal enum Notices { - /// Notices - internal static let header = L10n.tr("Localizable", "credits.sections.notices.header", fallback: "Notices") - } - } - } - internal enum DebugLog { - /// MARK: DiagnosticsView -> DebugLogView - internal static let title = L10n.tr("Localizable", "debug_log.title", fallback: "Debug log") - internal enum Buttons { - /// MARK: DiagnosticsView -> DebugLogView - internal static let copy = L10n.tr("Localizable", "debug_log.buttons.copy", fallback: "Copy") - } - } - internal enum Diagnostics { - /// MARK: ProfileView -> DiagnosticsView - internal static let title = L10n.tr("Localizable", "diagnostics.title", fallback: "Diagnostics") - internal enum Alerts { - internal enum MasksPrivateData { - internal enum Messages { - /// In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now. - internal static let mustReconnect = L10n.tr("Localizable", "diagnostics.alerts.masks_private_data.messages.must_reconnect", fallback: "In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.") - } - } - } - internal enum Items { - internal enum AppLog { - /// App - internal static let title = L10n.tr("Localizable", "diagnostics.items.app_log.title", fallback: "App") - } - internal enum MasksPrivateData { - /// Mask network data - internal static let caption = L10n.tr("Localizable", "diagnostics.items.masks_private_data.caption", fallback: "Mask network data") - } - internal enum ReportIssue { - /// Report connectivity issue - internal static let caption = L10n.tr("Localizable", "diagnostics.items.report_issue.caption", fallback: "Report connectivity issue") - } - internal enum ServerConfiguration { - /// Server configuration - internal static let caption = L10n.tr("Localizable", "diagnostics.items.server_configuration.caption", fallback: "Server configuration") - } - } - internal enum Sections { - internal enum DebugLog { - /// Masking status will be effective after reconnecting. Network data are hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless. - internal static let footer = L10n.tr("Localizable", "diagnostics.sections.debug_log.footer", fallback: "Masking status will be effective after reconnecting. Network data are hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless.") - } - } - } - internal enum Donate { - /// MARK: DonateView - internal static let title = L10n.tr("Localizable", "donate.title", fallback: "Donate") - internal enum Alerts { - internal enum Purchase { - internal enum Failure { - /// Unable to perform the donation. %@ - internal static func message(_ p1: Any) -> String { - return L10n.tr("Localizable", "donate.alerts.purchase.failure.message", String(describing: p1), fallback: "Unable to perform the donation. %@") - } - } - internal enum Success { - /// This means a lot to me and I really hope you keep using and promoting this app. - internal static let message = L10n.tr("Localizable", "donate.alerts.purchase.success.message", fallback: "This means a lot to me and I really hope you keep using and promoting this app.") - } - } - } - internal enum Items { - internal enum Loading { - /// Loading donations - internal static let caption = L10n.tr("Localizable", "donate.items.loading.caption", fallback: "Loading donations") - } - internal enum Purchasing { - /// Performing donation - internal static let caption = L10n.tr("Localizable", "donate.items.purchasing.caption", fallback: "Performing donation") - } - } - internal enum Sections { - internal enum OneTime { - /// If you want to display gratitude for my free work, here are a couple amounts you can donate instantly. - /// - /// You will only be charged once per donation, and you can donate multiple times. - internal static let footer = L10n.tr("Localizable", "donate.sections.one_time.footer", fallback: "If you want to display gratitude for my free work, here are a couple amounts you can donate instantly.\n\nYou will only be charged once per donation, and you can donate multiple times.") - /// One time - internal static let header = L10n.tr("Localizable", "donate.sections.one_time.header", fallback: "One time") - } - } - } - internal enum Endpoint { - internal enum Advanced { - /// Technical details - internal static let title = L10n.tr("Localizable", "endpoint.advanced.title", fallback: "Technical details") - internal enum Openvpn { - internal enum Items { - internal enum Cipher { - /// Cipher - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.cipher.caption", fallback: "Cipher") - } - internal enum Client { - /// Certificate - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.client.caption", fallback: "Certificate") - internal enum Value { - /// Not verified - internal static let disabled = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.client.value.disabled", fallback: "Not verified") - /// Verified - internal static let enabled = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.client.value.enabled", fallback: "Verified") - } - } - internal enum ClientKey { - /// Key - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.client_key.caption", fallback: "Key") - } - internal enum CompressionAlgorithm { - /// Algorithm - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.compression_algorithm.caption", fallback: "Algorithm") - internal enum Value { - /// Unsupported - internal static let other = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.compression_algorithm.value.other", fallback: "Unsupported") - } - } - internal enum CompressionFraming { - /// Framing - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.compression_framing.caption", fallback: "Framing") - } - internal enum Digest { - /// Authentication - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.digest.caption", fallback: "Authentication") - internal enum Value { - /// Embedded - internal static let embedded = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.digest.value.embedded", fallback: "Embedded") - } - } - internal enum Eku { - /// Extended verification - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.eku.caption", fallback: "Extended verification") - } - internal enum KeepAlive { - internal enum Value { - /// %d seconds - internal static func seconds(_ p1: Int) -> String { - return L10n.tr("Localizable", "endpoint.advanced.openvpn.items.keep_alive.value.seconds", p1, fallback: "%d seconds") - } - } - } - internal enum RandomEndpoint { - /// Randomize endpoint - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.random_endpoint.caption", fallback: "Randomize endpoint") - } - internal enum RandomHostname { - /// Randomize hostnames - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.random_hostname.caption", fallback: "Randomize hostnames") - } - internal enum RenegotiationSeconds { - /// Renegotiation - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.renegotiation_seconds.caption", fallback: "Renegotiation") - internal enum Value { - /// after %@ - internal static func after(_ p1: Any) -> String { - return L10n.tr("Localizable", "endpoint.advanced.openvpn.items.renegotiation_seconds.value.after", String(describing: p1), fallback: "after %@") - } - } - } - internal enum ResetOriginal { - /// Reset configuration - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.reset_original.caption", fallback: "Reset configuration") - } - internal enum Route { - /// Route - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.route.caption", fallback: "Route") - } - internal enum TlsWrapping { - /// Wrapping - internal static let caption = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.tls_wrapping.caption", fallback: "Wrapping") - internal enum Value { - /// Authentication - internal static let auth = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.tls_wrapping.value.auth", fallback: "Authentication") - /// Encryption - internal static let crypt = L10n.tr("Localizable", "endpoint.advanced.openvpn.items.tls_wrapping.value.crypt", fallback: "Encryption") - } - } - } - internal enum Sections { - internal enum Communication { - /// Communication - internal static let header = L10n.tr("Localizable", "endpoint.advanced.openvpn.sections.communication.header", fallback: "Communication") - } - internal enum Compression { - /// Compression - internal static let header = L10n.tr("Localizable", "endpoint.advanced.openvpn.sections.compression.header", fallback: "Compression") - } - internal enum Network { - /// Network - internal static let header = L10n.tr("Localizable", "endpoint.advanced.openvpn.sections.network.header", fallback: "Network") - } - internal enum Other { - /// Other - internal static let header = L10n.tr("Localizable", "endpoint.advanced.openvpn.sections.other.header", fallback: "Other") - } - internal enum Pull { - /// Pull from server - internal static let header = L10n.tr("Localizable", "endpoint.advanced.openvpn.sections.pull.header", fallback: "Pull from server") - } - internal enum Reset { - /// If you ended up with broken connectivity after changing the communication parameters, tap to revert to the original configuration. - internal static let footer = L10n.tr("Localizable", "endpoint.advanced.openvpn.sections.reset.footer", fallback: "If you ended up with broken connectivity after changing the communication parameters, tap to revert to the original configuration.") - } - } - } - } - internal enum Errors { - /// MARK: ProfileView -> EndpointView - internal static let endpointRequired = L10n.tr("Localizable", "endpoint.errors.endpoint_required", fallback: "Please select an endpoint") - } - internal enum Wireguard { - internal enum Items { - internal enum AllowedIp { - /// Allowed IP - internal static let caption = L10n.tr("Localizable", "endpoint.wireguard.items.allowed_ip.caption", fallback: "Allowed IP") - } - internal enum Peer { - /// MARK: ProfileView -> EndpointView - internal static let caption = L10n.tr("Localizable", "endpoint.wireguard.items.peer.caption", fallback: "Peer") - } - internal enum PresharedKey { - /// Preshared key - internal static let caption = L10n.tr("Localizable", "endpoint.wireguard.items.preshared_key.caption", fallback: "Preshared key") - } - } - } - } - internal enum Global { - internal enum Alerts { - internal enum Buttons { - /// Don't ask again - internal static let never = L10n.tr("Localizable", "global.alerts.buttons.never", fallback: "Don't ask again") - /// Remind me later - internal static let remind = L10n.tr("Localizable", "global.alerts.buttons.remind", fallback: "Remind me later") - } - } - internal enum Errors { - /// Missing account - internal static let missingAccount = L10n.tr("Localizable", "global.errors.missing_account", fallback: "Missing account") - /// Missing profile - internal static let missingProfile = L10n.tr("Localizable", "global.errors.missing_profile", fallback: "Missing profile") - /// Missing preset - internal static let missingProviderPreset = L10n.tr("Localizable", "global.errors.missing_provider_preset", fallback: "Missing preset") - /// Missing location - internal static let missingProviderServer = L10n.tr("Localizable", "global.errors.missing_provider_server", fallback: "Missing location") - /// Connection expired - internal static let tunnelExpired = L10n.tr("Localizable", "global.errors.tunnel_expired", fallback: "Connection expired") - } - internal enum Messages { - /// No e-mail account is configured. - internal static let emailNotConfigured = L10n.tr("Localizable", "global.messages.email_not_configured", fallback: "No e-mail account is configured.") - /// Passepartout is a user-friendly, open source OpenVPN / WireGuard client for iOS and macOS - internal static let share = L10n.tr("Localizable", "global.messages.share", fallback: "Passepartout is a user-friendly, open source OpenVPN / WireGuard client for iOS and macOS") - /// Passepartout is locked - internal static let unlockApp = L10n.tr("Localizable", "global.messages.unlock_app", fallback: "Passepartout is locked") - } - internal enum Placeholders { - /// My profile - internal static let profileName = L10n.tr("Localizable", "global.placeholders.profile_name", fallback: "My profile") - } - internal enum Strings { - /// Add - internal static let add = L10n.tr("Localizable", "global.strings.add", fallback: "Add") - /// Address - internal static let address = L10n.tr("Localizable", "global.strings.address", fallback: "Address") - /// Addresses - internal static let addresses = L10n.tr("Localizable", "global.strings.addresses", fallback: "Addresses") - /// Advanced - internal static let advanced = L10n.tr("Localizable", "global.strings.advanced", fallback: "Advanced") - /// Authentication - internal static let authentication = L10n.tr("Localizable", "global.strings.authentication", fallback: "Authentication") - /// Automatic - internal static let automatic = L10n.tr("Localizable", "global.strings.automatic", fallback: "Automatic") - /// Bytes - internal static let bytes = L10n.tr("Localizable", "global.strings.bytes", fallback: "Bytes") - /// MARK: Global - internal static let cancel = L10n.tr("Localizable", "global.strings.cancel", fallback: "Cancel") - /// Configuration - internal static let configuration = L10n.tr("Localizable", "global.strings.configuration", fallback: "Configuration") - /// Connect - internal static let connect = L10n.tr("Localizable", "global.strings.connect", fallback: "Connect") - /// Default - internal static let `default` = L10n.tr("Localizable", "global.strings.default", fallback: "Default") - /// Delete - internal static let delete = L10n.tr("Localizable", "global.strings.delete", fallback: "Delete") - /// Disabled - internal static let disabled = L10n.tr("Localizable", "global.strings.disabled", fallback: "Disabled") - /// Disconnect - internal static let disconnect = L10n.tr("Localizable", "global.strings.disconnect", fallback: "Disconnect") - /// Domain - internal static let domain = L10n.tr("Localizable", "global.strings.domain", fallback: "Domain") - /// Domains - internal static let domains = L10n.tr("Localizable", "global.strings.domains", fallback: "Domains") - /// Download - internal static let download = L10n.tr("Localizable", "global.strings.download", fallback: "Download") - /// Duplicate - internal static let duplicate = L10n.tr("Localizable", "global.strings.duplicate", fallback: "Duplicate") - /// Edit - internal static let edit = L10n.tr("Localizable", "global.strings.edit", fallback: "Edit") - /// Enabled - internal static let enabled = L10n.tr("Localizable", "global.strings.enabled", fallback: "Enabled") - /// Encryption - internal static let encryption = L10n.tr("Localizable", "global.strings.encryption", fallback: "Encryption") - /// Endpoint - internal static let endpoint = L10n.tr("Localizable", "global.strings.endpoint", fallback: "Endpoint") - /// Interface - internal static let interface = L10n.tr("Localizable", "global.strings.interface", fallback: "Interface") - /// Keep-alive - internal static let keepalive = L10n.tr("Localizable", "global.strings.keepalive", fallback: "Keep-alive") - /// Manual - internal static let manual = L10n.tr("Localizable", "global.strings.manual", fallback: "Manual") - /// Name - internal static let name = L10n.tr("Localizable", "global.strings.name", fallback: "Name") - /// Networks - internal static let networks = L10n.tr("Localizable", "global.strings.networks", fallback: "Networks") - /// Next - internal static let next = L10n.tr("Localizable", "global.strings.next", fallback: "Next") - /// None - internal static let `none` = L10n.tr("Localizable", "global.strings.none", fallback: "None") - /// MARK: Global - internal static let ok = L10n.tr("Localizable", "global.strings.ok", fallback: "OK") - /// Policy - internal static let policy = L10n.tr("Localizable", "global.strings.policy", fallback: "Policy") - /// Port - internal static let port = L10n.tr("Localizable", "global.strings.port", fallback: "Port") - /// Private key - internal static let privateKey = L10n.tr("Localizable", "global.strings.private_key", fallback: "Private key") - /// Profiles - internal static let profiles = L10n.tr("Localizable", "global.strings.profiles", fallback: "Profiles") - /// Protocol - internal static let `protocol` = L10n.tr("Localizable", "global.strings.protocol", fallback: "Protocol") - /// Protocols - internal static let protocols = L10n.tr("Localizable", "global.strings.protocols", fallback: "Protocols") - /// Provider - internal static let provider = L10n.tr("Localizable", "global.strings.provider", fallback: "Provider") - /// Providers - internal static let providers = L10n.tr("Localizable", "global.strings.providers", fallback: "Providers") - /// Proxy - internal static let proxy = L10n.tr("Localizable", "global.strings.proxy", fallback: "Proxy") - /// Public key - internal static let publicKey = L10n.tr("Localizable", "global.strings.public_key", fallback: "Public key") - /// Reconnect - internal static let reconnect = L10n.tr("Localizable", "global.strings.reconnect", fallback: "Reconnect") - /// Rename - internal static let rename = L10n.tr("Localizable", "global.strings.rename", fallback: "Rename") - /// Save - internal static let save = L10n.tr("Localizable", "global.strings.save", fallback: "Save") - /// Servers - internal static let servers = L10n.tr("Localizable", "global.strings.servers", fallback: "Servers") - /// Show - internal static let show = L10n.tr("Localizable", "global.strings.show", fallback: "Show") - /// Translations - internal static let translations = L10n.tr("Localizable", "global.strings.translations", fallback: "Translations") - /// Uninstall - internal static let uninstall = L10n.tr("Localizable", "global.strings.uninstall", fallback: "Uninstall") - /// Unknown - internal static let unknown = L10n.tr("Localizable", "global.strings.unknown", fallback: "Unknown") - } - } - internal enum Menu { - internal enum All { - internal enum About { - /// About %@ - internal static func title(_ p1: Any) -> String { - return L10n.tr("Localizable", "menu.all.about.title", String(describing: p1), fallback: "About %@") - } - } - internal enum Share { - /// Share - internal static let title = L10n.tr("Localizable", "menu.all.share.title", fallback: "Share") - } - internal enum Support { - /// MARK: Menus - internal static let title = L10n.tr("Localizable", "menu.all.support.title", fallback: "Support") - } - } - internal enum Contextual { - /// Invite - internal static let shareGeneric = L10n.tr("Localizable", "menu.contextual.share_generic", fallback: "Invite") - /// Tweet - internal static let shareTwitter = L10n.tr("Localizable", "menu.contextual.share_twitter", fallback: "Tweet") - internal enum AddProfile { - /// From Files - internal static let fromFiles = L10n.tr("Localizable", "menu.contextual.add_profile.from_files", fallback: "From Files") - /// From text - internal static let fromText = L10n.tr("Localizable", "menu.contextual.add_profile.from_text", fallback: "From text") - /// Add %@ - internal static func imported(_ p1: Any) -> String { - return L10n.tr("Localizable", "menu.contextual.add_profile.imported", String(describing: p1), fallback: "Add %@") - } - } - internal enum Support { - /// Community - internal static let joinCommunity = L10n.tr("Localizable", "menu.contextual.support.join_community", fallback: "Community") - /// Review - internal static let writeReview = L10n.tr("Localizable", "menu.contextual.support.write_review", fallback: "Review") - } - } - internal enum System { - internal enum Quit { - /// Quit %@ - internal static func title(_ p1: Any) -> String { - return L10n.tr("Localizable", "menu.system.quit.title", String(describing: p1), fallback: "Quit %@") - } - internal enum Messages { - /// The VPN, if enabled, will still run in the background. Do you want to quit? - internal static let confirm = L10n.tr("Localizable", "menu.system.quit.messages.confirm", fallback: "The VPN, if enabled, will still run in the background. Do you want to quit?") - } - } - } - } - internal enum NetworkSettings { - /// MARK: ProfileView -> NetworkSettingsView - internal static let title = L10n.tr("Localizable", "network_settings.title", fallback: "Network settings") - internal enum Gateway { - /// Default gateway - internal static let title = L10n.tr("Localizable", "network_settings.gateway.title", fallback: "Default gateway") - } - internal enum Items { - internal enum AddDnsDomain { - /// Add search domain - internal static let caption = L10n.tr("Localizable", "network_settings.items.add_dns_domain.caption", fallback: "Add search domain") - } - internal enum AddDnsServer { - /// Add address - internal static let caption = L10n.tr("Localizable", "network_settings.items.add_dns_server.caption", fallback: "Add address") - } - internal enum AddProxyBypass { - /// Add bypass domain - internal static let caption = L10n.tr("Localizable", "network_settings.items.add_proxy_bypass.caption", fallback: "Add bypass domain") - } - internal enum ProxyBypass { - /// Bypass domain - internal static let caption = L10n.tr("Localizable", "network_settings.items.proxy_bypass.caption", fallback: "Bypass domain") - } - } - internal enum Proxy { - internal enum Items { - internal enum BypassDomains { - /// Bypass domains - internal static let caption = L10n.tr("Localizable", "network_settings.proxy.items.bypass_domains.caption", fallback: "Bypass domains") - } - } - } - internal enum Sections { - internal enum Choices { - /// Override - internal static let header = L10n.tr("Localizable", "network_settings.sections.choices.header", fallback: "Override") - } - } - } - internal enum OnDemand { - /// MARK: ProfileView -> OnDemandView - internal static let title = L10n.tr("Localizable", "on_demand.title", fallback: "On demand") - internal enum Items { - internal enum Active { - /// Trust - internal static let caption = L10n.tr("Localizable", "on_demand.items.active.caption", fallback: "Trust") - } - internal enum AddSsid { - /// Add Wi-Fi - internal static let caption = L10n.tr("Localizable", "on_demand.items.add_ssid.caption", fallback: "Add Wi-Fi") - } - internal enum Ethernet { - /// Wired connections - internal static let caption = L10n.tr("Localizable", "on_demand.items.ethernet.caption", fallback: "Wired connections") - } - internal enum Mobile { - /// Cellular network - internal static let caption = L10n.tr("Localizable", "on_demand.items.mobile.caption", fallback: "Cellular network") - } - internal enum Policy { - /// Trust disables VPN - internal static let caption = L10n.tr("Localizable", "on_demand.items.policy.caption", fallback: "Trust disables VPN") - } - } - internal enum Policy { - /// All networks - internal static let any = L10n.tr("Localizable", "on_demand.policy.any", fallback: "All networks") - /// Exclude - internal static let excluding = L10n.tr("Localizable", "on_demand.policy.excluding", fallback: "Exclude") - /// Include - internal static let including = L10n.tr("Localizable", "on_demand.policy.including", fallback: "Include") - } - internal enum Sections { - internal enum Policy { - /// Activate the VPN %@. - internal static func footer(_ p1: Any) -> String { - return L10n.tr("Localizable", "on_demand.sections.policy.footer", String(describing: p1), fallback: "Activate the VPN %@.") - } - internal enum Footer { - /// in any network - internal static let any = L10n.tr("Localizable", "on_demand.sections.policy.footer.any", fallback: "in any network") - /// except in the networks below - internal static let excluding = L10n.tr("Localizable", "on_demand.sections.policy.footer.excluding", fallback: "except in the networks below") - /// only in the networks below - internal static let including = L10n.tr("Localizable", "on_demand.sections.policy.footer.including", fallback: "only in the networks below") - } - } - } - } - internal enum Organizer { - internal enum Alerts { - internal enum Reddit { - /// Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like. - /// - /// It's also a great way to show you care about this project. - internal static let message = L10n.tr("Localizable", "organizer.alerts.reddit.message", fallback: "Did you know that Passepartout has a subreddit? Subscribe for updates or to discuss issues, features, new platforms or whatever you like.\n\nIt's also a great way to show you care about this project.") - internal enum Buttons { - /// Subscribe now! - internal static let subscribe = L10n.tr("Localizable", "organizer.alerts.reddit.buttons.subscribe", fallback: "Subscribe now!") - } - } - internal enum RemoveProfile { - /// Are you sure you want to delete profile %@? - internal static func message(_ p1: Any) -> String { - return L10n.tr("Localizable", "organizer.alerts.remove_profile.message", String(describing: p1), fallback: "Are you sure you want to delete profile %@?") - } - } - } - internal enum Empty { - /// MARK: OrganizerView - internal static let noProfiles = L10n.tr("Localizable", "organizer.empty.no_profiles", fallback: "No profiles") - } - internal enum Sections { - /// MARK: OrganizerView - internal static let active = L10n.tr("Localizable", "organizer.sections.active", fallback: "In use") - internal enum Tv { - internal enum ProfilesList { - internal enum Header { - /// Open Passepartout on your iOS or macOS device and enable the "Apple TV" toggle of a profile to make it appear here. - internal static let p1 = L10n.tr("Localizable", "organizer.sections.tv.profiles_list.header.p1", fallback: "Open Passepartout on your iOS or macOS device and enable the \"Apple TV\" toggle of a profile to make it appear here.") - } - } - } - } - } - internal enum Paywall { - /// MARK: PaywallView - internal static let title = L10n.tr("Localizable", "paywall.title", fallback: "Purchase") - internal enum Alerts { - internal enum Purchase { - internal enum Appletv { - internal enum Success { - /// 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. - internal static let message = L10n.tr("Localizable", "paywall.alerts.purchase.appletv.success.message", fallback: "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.") - } - } - } - } - internal enum Items { - internal enum FullVersion { - /// All providers (including future ones) - /// %@ - internal static func extraDescription(_ p1: Any) -> String { - return L10n.tr("Localizable", "paywall.items.full_version.extra_description", String(describing: p1), fallback: "All providers (including future ones)\n%@") - } - } - internal enum Loading { - /// Loading products - internal static let caption = L10n.tr("Localizable", "paywall.items.loading.caption", fallback: "Loading products") - } - internal enum Restore { - /// If you bought this app or feature in the past, you can restore your purchases and this screen won't show again. - internal static let description = L10n.tr("Localizable", "paywall.items.restore.description", fallback: "If you bought this app or feature in the past, you can restore your purchases and this screen won't show again.") - /// Restore purchases - internal static let title = L10n.tr("Localizable", "paywall.items.restore.title", fallback: "Restore purchases") - } - } - internal enum Sections { - internal enum Products { - /// Every product is a one-time purchase. Provider purchases do not include a VPN subscription. - internal static let footer = L10n.tr("Localizable", "paywall.sections.products.footer", fallback: "Every product is a one-time purchase. Provider purchases do not include a VPN subscription.") - } - } - } - internal enum Preferences { - /// MARK: PreferencesView (macOS) - internal static let title = L10n.tr("Localizable", "preferences.title", fallback: "Preferences") - internal enum Items { - internal enum ConfirmQuit { - /// Confirm quit - internal static let caption = L10n.tr("Localizable", "preferences.items.confirm_quit.caption", fallback: "Confirm quit") - /// Check to present a quit confirmation alert. - internal static let footer = L10n.tr("Localizable", "preferences.items.confirm_quit.footer", fallback: "Check to present a quit confirmation alert.") - } - internal enum LaunchesOnLogin { - /// Launch on login - internal static let caption = L10n.tr("Localizable", "preferences.items.launches_on_login.caption", fallback: "Launch on login") - /// Check to automatically launch the app on boot or login. - internal static let footer = L10n.tr("Localizable", "preferences.items.launches_on_login.footer", fallback: "Check to automatically launch the app on boot or login.") - } - } - internal enum Sections { - internal enum General { - /// General - internal static let header = L10n.tr("Localizable", "preferences.sections.general.header", fallback: "General") - } - } - } - internal enum Profile { - internal enum Alerts { - internal enum ReconnectVpn { - /// Do you want to reconnect to the VPN? - internal static let message = L10n.tr("Localizable", "profile.alerts.reconnect_vpn.message", fallback: "Do you want to reconnect to the VPN?") - } - internal enum Rename { - /// Rename profile - internal static let title = L10n.tr("Localizable", "profile.alerts.rename.title", fallback: "Rename profile") - } - internal enum TestConnectivity { - /// Connectivity - internal static let title = L10n.tr("Localizable", "profile.alerts.test_connectivity.title", fallback: "Connectivity") - internal enum Messages { - /// Your device has no Internet connectivity, please review your profile parameters. - internal static let failure = L10n.tr("Localizable", "profile.alerts.test_connectivity.messages.failure", fallback: "Your device has no Internet connectivity, please review your profile parameters.") - /// Your device is connected to the Internet! - internal static let success = L10n.tr("Localizable", "profile.alerts.test_connectivity.messages.success", fallback: "Your device is connected to the Internet!") - } - } - internal enum UninstallVpn { - /// Do you really want to erase the VPN configuration from your device settings? This may fix some broken VPN states and will not affect your provider and host profiles. - internal static let message = L10n.tr("Localizable", "profile.alerts.uninstall_vpn.message", fallback: "Do you really want to erase the VPN configuration from your device settings? This may fix some broken VPN states and will not affect your provider and host profiles.") - } - } - internal enum Items { - internal enum Category { - /// Category - internal static let caption = L10n.tr("Localizable", "profile.items.category.caption", fallback: "Category") - } - internal enum ConnectionStatus { - /// Status - internal static let caption = L10n.tr("Localizable", "profile.items.connection_status.caption", fallback: "Status") - } - internal enum DataCount { - /// Exchanged data - internal static let caption = L10n.tr("Localizable", "profile.items.data_count.caption", fallback: "Exchanged data") - } - internal enum ExpiresAt { - /// Expiration - internal static let caption = L10n.tr("Localizable", "profile.items.expires_at.caption", fallback: "Expiration") - } - internal enum OnlyShowsFavorites { - /// Only show favorite locations - internal static let caption = L10n.tr("Localizable", "profile.items.only_shows_favorites.caption", fallback: "Only show favorite locations") - } - internal enum Provider { - internal enum Refresh { - /// Refresh infrastructure - internal static let caption = L10n.tr("Localizable", "profile.items.provider.refresh.caption", fallback: "Refresh infrastructure") - } - } - internal enum RandomizesServer { - /// Randomize server - internal static let caption = L10n.tr("Localizable", "profile.items.randomizes_server.caption", fallback: "Randomize server") - } - internal enum TvSharing { - internal enum Caption { - /// Limited to %d minutes - internal static func limited(_ p1: Int) -> String { - return L10n.tr("Localizable", "profile.items.tv_sharing.caption.limited", p1, fallback: "Limited to %d minutes") - } - } - } - internal enum UseProfile { - /// Use this profile - internal static let caption = L10n.tr("Localizable", "profile.items.use_profile.caption", fallback: "Use this profile") - } - internal enum Vpn { - internal enum TurnOff { - /// Disable VPN - internal static let caption = L10n.tr("Localizable", "profile.items.vpn.turn_off.caption", fallback: "Disable VPN") - } - internal enum TurnOn { - /// Enable VPN - internal static let caption = L10n.tr("Localizable", "profile.items.vpn.turn_on.caption", fallback: "Enable VPN") - } - } - internal enum VpnResolvesHostname { - /// Resolve provider hostname - internal static let caption = L10n.tr("Localizable", "profile.items.vpn_resolves_hostname.caption", fallback: "Resolve provider hostname") - } - internal enum VpnService { - /// Enabled - internal static let caption = L10n.tr("Localizable", "profile.items.vpn_service.caption", fallback: "Enabled") - } - internal enum VpnSurvivesSleep { - /// Keep alive on sleep - internal static let caption = L10n.tr("Localizable", "profile.items.vpn_survives_sleep.caption", fallback: "Keep alive on sleep") - } - } - internal enum Sections { - internal enum Feedback { - /// Feedback - internal static let header = L10n.tr("Localizable", "profile.sections.feedback.header", fallback: "Feedback") - } - internal enum ProviderInfrastructure { - /// Last updated on %@. - internal static func footer(_ p1: Any) -> String { - return L10n.tr("Localizable", "profile.sections.provider_infrastructure.footer", String(describing: p1), fallback: "Last updated on %@.") - } - } - internal enum Status { - /// Connection - internal static let header = L10n.tr("Localizable", "profile.sections.status.header", fallback: "Connection") - } - internal enum Tv { - internal enum Footer { - /// Profiles are encrypted and made available to your Apple TV via iCloud. - internal static let encryption = L10n.tr("Localizable", "profile.sections.tv.footer.encryption", fallback: "Profiles are encrypted and made available to your Apple TV via iCloud.") - internal enum Restricted { - /// However, the connection will expire after %d minutes. - internal static func p1(_ p1: Int) -> String { - return L10n.tr("Localizable", "profile.sections.tv.footer.restricted.p1", p1, fallback: "However, the connection will expire after %d minutes.") - } - /// Purchase to drop the restriction. - internal static let p2 = L10n.tr("Localizable", "profile.sections.tv.footer.restricted.p2", fallback: "Purchase to drop the restriction.") - } - } - } - internal enum Vpn { - /// The connection will be established whenever necessary. - internal static let footer = L10n.tr("Localizable", "profile.sections.vpn.footer", fallback: "The connection will be established whenever necessary.") - } - internal enum VpnResolvesHostname { - /// 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. - internal static let footer = L10n.tr("Localizable", "profile.sections.vpn_resolves_hostname.footer", fallback: "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.") - } - internal enum VpnSurvivesSleep { - /// Disable to improve battery usage, at the expense of occasional slowdowns due to wake-up reconnections. - internal static let footer = L10n.tr("Localizable", "profile.sections.vpn_survives_sleep.footer", fallback: "Disable to improve battery usage, at the expense of occasional slowdowns due to wake-up reconnections.") - } - } - internal enum Welcome { - /// MARK: ProfileView - internal static let message = L10n.tr("Localizable", "profile.welcome.message", fallback: "Welcome to Passepartout!\n\nUse the organizer to add a new profile.") - } - } - internal enum Provider { - internal enum Location { - /// MARK: ProfileView -> Provider*View - internal static let title = L10n.tr("Localizable", "provider.location.title", fallback: "Location") - internal enum Actions { - /// Favorite - internal static let favorite = L10n.tr("Localizable", "provider.location.actions.favorite", fallback: "Favorite") - /// Unfavorite - internal static let unfavorite = L10n.tr("Localizable", "provider.location.actions.unfavorite", fallback: "Unfavorite") - } - internal enum Sections { - internal enum EmptyFavorites { - /// Swipe left on a location to add or remove it from Favorites. - internal static let footer = L10n.tr("Localizable", "provider.location.sections.empty_favorites.footer", fallback: "Swipe left on a location to add or remove it from Favorites.") - } - } - } - internal enum Preset { - /// Preset - internal static let title = L10n.tr("Localizable", "provider.preset.title", fallback: "Preset") - } - } - internal enum ReportIssue { - internal enum Alert { - /// MARK: DiagnosticsView -> ReportIssueView - internal static let title = L10n.tr("Localizable", "report_issue.alert.title", fallback: "Report issue") - } - } - internal enum Settings { - /// MARK: SettingsView - internal static let title = L10n.tr("Localizable", "settings.title", fallback: "Settings") - internal enum Items { - internal enum Donate { - /// Make a donation - internal static let caption = L10n.tr("Localizable", "settings.items.donate.caption", fallback: "Make a donation") - } - internal enum EraseCloudStore { - /// Erase iCloud store - internal static let caption = L10n.tr("Localizable", "settings.items.erase_cloud_store.caption", fallback: "Erase iCloud store") - } - internal enum LocksInBackground { - /// Lock app access - internal static let caption = L10n.tr("Localizable", "settings.items.locks_in_background.caption", fallback: "Lock app access") - } - internal enum ShouldEnableCloudSyncing { - /// Sync with iCloud - internal static let caption = L10n.tr("Localizable", "settings.items.should_enable_cloud_syncing.caption", fallback: "Sync with iCloud") - } - } - internal enum Sections { - internal enum Icloud { - /// Disable sync to allow erase. To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles. - internal static let footer = L10n.tr("Localizable", "settings.sections.icloud.footer", fallback: "Disable sync to allow erase. To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles.") - } - } - } - internal enum Shortcuts { - internal enum Add { - /// MARK: ShortcutsView - internal static let title = L10n.tr("Localizable", "shortcuts.add.title", fallback: "Add shortcut") - internal enum Alerts { - internal enum NoProfiles { - /// There is no profile to connect to. - internal static let message = L10n.tr("Localizable", "shortcuts.add.alerts.no_profiles.message", fallback: "There is no profile to connect to.") - } - } - internal enum Items { - internal enum Connect { - /// Connect to - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.connect.caption", fallback: "Connect to") - } - internal enum DisableVpn { - /// Disable VPN - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.disable_vpn.caption", fallback: "Disable VPN") - } - internal enum EnableVpn { - /// Enable VPN - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.enable_vpn.caption", fallback: "Enable VPN") - } - internal enum TrustCellular { - /// Trust cellular network - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.trust_cellular.caption", fallback: "Trust cellular network") - } - internal enum TrustCurrentWifi { - /// Trust current Wi-Fi - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.trust_current_wifi.caption", fallback: "Trust current Wi-Fi") - } - internal enum UntrustCellular { - /// Untrust cellular network - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.untrust_cellular.caption", fallback: "Untrust cellular network") - } - internal enum UntrustCurrentWifi { - /// Untrust current Wi-Fi - internal static let caption = L10n.tr("Localizable", "shortcuts.add.items.untrust_current_wifi.caption", fallback: "Untrust current Wi-Fi") - } - } - internal enum Sections { - internal enum Cellular { - /// Cellular - internal static let header = L10n.tr("Localizable", "shortcuts.add.sections.cellular.header", fallback: "Cellular") - } - internal enum Wifi { - /// Wi-Fi - internal static let header = L10n.tr("Localizable", "shortcuts.add.sections.wifi.header", fallback: "Wi-Fi") - } - } - } - internal enum Edit { - /// Manage shortcuts - internal static let title = L10n.tr("Localizable", "shortcuts.edit.title", fallback: "Manage shortcuts") - internal enum Items { - internal enum AddShortcut { - /// Add shortcut - internal static let caption = L10n.tr("Localizable", "shortcuts.edit.items.add_shortcut.caption", fallback: "Add shortcut") - } - } - internal enum Sections { - internal enum Add { - /// Get help from Siri to speed up your most common interactions with the app. - internal static let footer = L10n.tr("Localizable", "shortcuts.edit.sections.add.footer", fallback: "Get help from Siri to speed up your most common interactions with the app.") - } - internal enum All { - /// Existing shortcuts - internal static let header = L10n.tr("Localizable", "shortcuts.edit.sections.all.header", fallback: "Existing shortcuts") - } - } - } - } - internal enum Tunnelkit { - internal enum Errors { - /// Unable to parse the provided configuration file (%@). - internal static func parsing(_ p1: Any) -> String { - return L10n.tr("Localizable", "tunnelkit.errors.parsing", String(describing: p1), fallback: "Unable to parse the provided configuration file (%@).") - } - internal enum Openvpn { - /// Unable to decrypt private key. - internal static let decryption = L10n.tr("Localizable", "tunnelkit.errors.openvpn.decryption", fallback: "Unable to decrypt private key.") - /// The configuration file contains a malformed option (%@). - internal static func malformed(_ p1: Any) -> String { - return L10n.tr("Localizable", "tunnelkit.errors.openvpn.malformed", String(describing: p1), fallback: "The configuration file contains a malformed option (%@).") - } - /// Please enter the encryption passphrase. - internal static let passphraseRequired = L10n.tr("Localizable", "tunnelkit.errors.openvpn.passphrase_required", fallback: "Please enter the encryption passphrase.") - /// The configuration file is correct but contains a potentially unsupported option (%@). - /// - /// Connectivity may break depending on server settings. - internal static func potentiallyUnsupportedOption(_ p1: Any) -> String { - return L10n.tr("Localizable", "tunnelkit.errors.openvpn.potentially_unsupported_option", String(describing: p1), fallback: "The configuration file is correct but contains a potentially unsupported option (%@).\n\nConnectivity may break depending on server settings.") - } - /// The configuration file lacks a required option (%@). - internal static func requiredOption(_ p1: Any) -> String { - return L10n.tr("Localizable", "tunnelkit.errors.openvpn.required_option", String(describing: p1), fallback: "The configuration file lacks a required option (%@).") - } - /// The configuration file contains an unsupported option (%@). - internal static func unsupportedOption(_ p1: Any) -> String { - return L10n.tr("Localizable", "tunnelkit.errors.openvpn.unsupported_option", String(describing: p1), fallback: "The configuration file contains an unsupported option (%@).") - } - } - internal enum Vpn { - /// Auth failed - internal static let auth = L10n.tr("Localizable", "tunnelkit.errors.vpn.auth", fallback: "Auth failed") - /// Compression unsupported - internal static let compression = L10n.tr("Localizable", "tunnelkit.errors.vpn.compression", fallback: "Compression unsupported") - /// DNS failed - internal static let dns = L10n.tr("Localizable", "tunnelkit.errors.vpn.dns", fallback: "DNS failed") - /// Encryption failed - internal static let encryption = L10n.tr("Localizable", "tunnelkit.errors.vpn.encryption", fallback: "Encryption failed") - /// No gateway - internal static let gateway = L10n.tr("Localizable", "tunnelkit.errors.vpn.gateway", fallback: "No gateway") - /// Network changed - internal static let network = L10n.tr("Localizable", "tunnelkit.errors.vpn.network", fallback: "Network changed") - /// Missing routing - internal static let routing = L10n.tr("Localizable", "tunnelkit.errors.vpn.routing", fallback: "Missing routing") - /// Server shutdown - internal static let shutdown = L10n.tr("Localizable", "tunnelkit.errors.vpn.shutdown", fallback: "Server shutdown") - /// Timeout - internal static let timeout = L10n.tr("Localizable", "tunnelkit.errors.vpn.timeout", fallback: "Timeout") - /// TLS failed - internal static let tls = L10n.tr("Localizable", "tunnelkit.errors.vpn.tls", fallback: "TLS failed") - } - } - internal enum Vpn { - /// Active - internal static let active = L10n.tr("Localizable", "tunnelkit.vpn.active", fallback: "Active") - /// MARK: TunnelKit - internal static let connecting = L10n.tr("Localizable", "tunnelkit.vpn.connecting", fallback: "Connecting") - /// Disabled - internal static let disabled = L10n.tr("Localizable", "tunnelkit.vpn.disabled", fallback: "Disabled") - /// Disconnecting - internal static let disconnecting = L10n.tr("Localizable", "tunnelkit.vpn.disconnecting", fallback: "Disconnecting") - /// Inactive - internal static let inactive = L10n.tr("Localizable", "tunnelkit.vpn.inactive", fallback: "Inactive") - /// Off - internal static let unused = L10n.tr("Localizable", "tunnelkit.vpn.unused", fallback: "Off") - } - } - internal enum Version { - /// MARK: AboutView -> VersionView - internal static let title = L10n.tr("Localizable", "version.title", fallback: "Version") - internal enum Labels { - /// Passepartout and TunnelKit are written and maintained by Davide De Rosa (keeshux). - /// - /// Source code for Passepartout and TunnelKit is publicly available on GitHub under the GPLv3, you can find links in the home page. - internal static let intro = L10n.tr("Localizable", "version.labels.intro", fallback: "Passepartout and TunnelKit are written and maintained by Davide De Rosa (keeshux).\n\nSource code for Passepartout and TunnelKit is publicly available on GitHub under the GPLv3, you can find links in the home page.") - } - } -} -// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length -// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces - -// MARK: - Implementation Details - -extension L10n { - private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String { - let format = Bundle.main.localizedString(forKey: key, value: value, table: table) - return String(format: format, locale: Locale.current, arguments: args) - } -} diff --git a/Passepartout/AppShared/Mac/MacUtils.swift b/Passepartout/AppShared/Mac/MacUtils.swift deleted file mode 100644 index 8f926975..00000000 --- a/Passepartout/AppShared/Mac/MacUtils.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// MacUtils.swift -// Passepartout -// -// Created by Davide De Rosa on 6/25/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -@objc -public protocol MacUtils { - var isStartedByLauncher: Bool { get } - - func sendAppToBackground() -} - -@objc -public protocol MacUtilsDelegate { -} diff --git a/Passepartout/AppShared/Mac/Managers/LightProfileManager.swift b/Passepartout/AppShared/Mac/Managers/LightProfileManager.swift deleted file mode 100644 index b219ef9e..00000000 --- a/Passepartout/AppShared/Mac/Managers/LightProfileManager.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// LightProfileManager.swift -// Passepartout -// -// Created by Davide De Rosa on 7/3/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -@objc(LightProfile) -public protocol LightProfile { - var id: UUID { get } - - var name: String { get } - - var vpnProtocol: String { get } - - var isActive: Bool { get } - - var providerName: String? { get } - - var providerServer: LightProviderServer? { get } -} - -extension LightProfile { - public var isProvider: Bool { - providerName != nil - } -} - -@MainActor -@objc -public protocol LightProfileManager { - var hasProfiles: Bool { get } - - var profiles: [LightProfile] { get } - - var activeProfileId: UUID? { get } - - var activeProfileName: String? { get } - - var delegate: LightProfileManagerDelegate? { get set } -} - -@objc -public protocol LightProfileManagerDelegate { - func didUpdateProfiles() -} diff --git a/Passepartout/AppShared/Mac/Managers/LightProviderManager.swift b/Passepartout/AppShared/Mac/Managers/LightProviderManager.swift deleted file mode 100644 index 65c10c02..00000000 --- a/Passepartout/AppShared/Mac/Managers/LightProviderManager.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// DefaultLightProviderManager.swift -// Passepartout -// -// Created by Davide De Rosa on 7/7/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -@objc(LightProviderCategory) -public protocol LightProviderCategory { - var name: String { get } - - var locations: [LightProviderLocation] { get } -} - -@objc(LightProviderLocation) -public protocol LightProviderLocation { - var description: String { get } - - var id: String { get } - - var countryCode: String { get } - - var servers: [LightProviderServer] { get } -} - -@objc(LightProviderServer) -public protocol LightProviderServer { - var description: String { get } - - var longDescription: String { get } - - var categoryName: String { get } - - var locationId: String { get } - - var serverId: String { get } -} - -@MainActor -@objc -public protocol LightProviderManager { - var delegate: LightProviderManagerDelegate? { get set } - - func categories(_ name: String, vpnProtocol: String) -> [LightProviderCategory] - - func downloadIfNeeded(_ name: String, vpnProtocol: String) -} - -@objc -public protocol LightProviderManagerDelegate { - func didUpdateProviders() -} diff --git a/Passepartout/AppShared/Mac/Managers/LightVPNManager.swift b/Passepartout/AppShared/Mac/Managers/LightVPNManager.swift deleted file mode 100644 index e0a3b4e8..00000000 --- a/Passepartout/AppShared/Mac/Managers/LightVPNManager.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// LightVPNManager.swift -// Passepartout -// -// Created by Davide De Rosa on 7/3/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -@objc(LightVPNStatus) -public enum LightVPNStatus: Int { - case connecting - - case connected - - case disconnecting - - case disconnected -} - -@MainActor -@objc -public protocol LightVPNManager { - var isEnabled: Bool { get } - - var vpnStatus: LightVPNStatus { get } - - func connect(with profileId: UUID) - - func connect(with profileId: UUID, to serverId: String) - - func disconnect() - - func toggle() - - func reconnect() - - func addDelegate(_ delegate: LightVPNManagerDelegate, withIdentifier identifier: String) - - func removeDelegate(withIdentifier identifier: String) -} - -@objc -public protocol LightVPNManagerDelegate { - func didUpdateState(isEnabled: Bool, vpnStatus: LightVPNStatus) -} diff --git a/Passepartout/AppShared/Menu/MenuBuilder.swift b/Passepartout/AppShared/Menu/MenuBuilder.swift deleted file mode 100644 index cb4918be..00000000 --- a/Passepartout/AppShared/Menu/MenuBuilder.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// MenuBuilder.swift -// Passepartout -// -// Created by Davide De Rosa on 6/24/22. -// Copyright (c) 2022 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -@objc(MenuBuilder) -public protocol MenuBuilder: NSObjectProtocol { - weak var delegate: MenuDelegate? { get set } - - init() - - func sendAppToBackground() - - func buildMenu() -} diff --git a/Passepartout/AppShared/Menu/MenuDelegate.swift b/Passepartout/AppShared/Menu/MenuDelegate.swift deleted file mode 100644 index 47a9854b..00000000 --- a/Passepartout/AppShared/Menu/MenuDelegate.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// MenuDelegate.swift -// Passepartout -// -// Created by Davide De Rosa on 6/24/22. -// Copyright (c) 2022 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -@objc(MenuDelegate) -public protocol MenuDelegate { - func toggleForegroundClicked(isForeground: Bool) -} diff --git a/Config.xcconfig b/Passepartout/Config.xcconfig similarity index 79% rename from Config.xcconfig rename to Passepartout/Config.xcconfig index 64dc7bcc..5bdb0afb 100644 --- a/Config.xcconfig +++ b/Passepartout/Config.xcconfig @@ -2,7 +2,7 @@ // Config.xcconfig // Passepartout // -// Created by Davide De Rosa on 5/24/19. +// Created by Davide De Rosa on 7/1/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -26,13 +26,16 @@ // Configuration settings file format documentation can be found at: // https://help.apple.com/xcode/#/dev745c5c974 -CFG_TEAM_ID = DTDYD63ZX9 CFG_APP_ID = com.algoritmico.ios.Passepartout -CFG_MAC_ID = com.algoritmico.ios.PassepartoutMac -CFG_LAUNCHER_ID = com.algoritmico.ios.PassepartoutLauncher -CFG_GROUP_ID = com.algoritmico.Passepartout -CFG_APPSTORE_ID = 1433648537 +CFG_APP_STORE_ID = 1433648537 CFG_COPYRIGHT = Copyright © 2024 Davide De Rosa. All rights reserved. +CFG_DISPLAY_NAME = Passepartout +CFG_GROUP_ID = group.$(CFG_RAW_GROUP_ID) +CFG_IAP_BUNDLE_PREFIX = com.algoritmico.ios.Passepartout +CFG_PROFILES_CONTAINER_NAME = Profiles-v3 +CFG_RAW_GROUP_ID = com.algoritmico.Passepartout +CFG_TEAM_ID = DTDYD63ZX9 +CFG_TUNNEL_ID = $(CFG_APP_ID).Tunnel PATH = $(PATH):/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin CUSTOM_SCRIPT_PATH = $(PATH) diff --git a/Passepartout/Constants.swift b/Passepartout/Constants.swift deleted file mode 100644 index f4253a67..00000000 --- a/Passepartout/Constants.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// Constants.swift -// Passepartout -// -// Created by Davide De Rosa on 6/7/18. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -enum Constants { - static func bundleConfig(_ key: String, in bundle: Bundle? = nil) -> T { - guard let config = (bundle ?? .main).infoDictionary?["com.algoritmico.Passepartout.config"] as? [String: Any] else { - fatalError("Unable to find config bundle") - } - guard let value = config[key] as? T else { - fatalError("Missing \(key) from config bundle") - } - return value - } - - enum Global { - static let appName = "Passepartout" - - static let appVersionNumber = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String - - static let appBuildNumber = Int(Bundle.main.infoDictionary![kCFBundleVersionKey as String] as! String)! - - static let appVersionString = "\(appVersionNumber) (\(appBuildNumber))" - } - - enum Tunnel { - static let expirationTimeIntervalKey = "ExpirationTimeInterval" - } -} diff --git a/Passepartout/Launcher/AppDelegate.swift b/Passepartout/Launcher/AppDelegate.swift deleted file mode 100644 index 39ddfc39..00000000 --- a/Passepartout/Launcher/AppDelegate.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// AppDelegate.swift -// Passepartout -// -// Created by Davide De Rosa on 6/25/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import AppKit -import Foundation - -final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { - private let appURL = Constants.Launcher.appURL - - private var isAppRunning: Bool { - NSWorkspace.shared.runningApplications.contains { - $0.bundleIdentifier == Constants.Launcher.appId - } - } - - func applicationDidFinishLaunching(_ notification: Notification) { - guard !isAppRunning else { - NSApp.terminate(self) - return - } - - let cfg = NSWorkspace.OpenConfiguration() - cfg.hides = true - cfg.activates = false - cfg.addsToRecentItems = false - NSWorkspace.shared.openApplication(at: appURL, configuration: cfg) { _, error in - if let error = error { - NSLog("Unable to launch main app: \(error)") - return - } - NSApp.terminate(self) - } - } -} diff --git a/Passepartout/Launcher/Constants/Constants+Launcher.swift b/Passepartout/Launcher/Constants/Constants+Launcher.swift deleted file mode 100644 index fb48bf2f..00000000 --- a/Passepartout/Launcher/Constants/Constants+Launcher.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Constants+Launcher.swift -// Passepartout -// -// Created by Davide De Rosa on 6/25/22. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation - -extension Constants { - enum Launcher { - static let appId: String = bundleConfig("app_id") - - private static let appPath: String = { - let path = Bundle.main.bundlePath as NSString - var components = path.pathComponents - - // Passepartout.app/Contents/Library/LoginItems/PassepartoutLauncher.app - components.removeLast(4) - - return NSString.path(withComponents: components) - }() - - static let appURL = URL(fileURLWithPath: appPath) - } -} diff --git a/Passepartout/Launcher/Info.plist b/Passepartout/Launcher/Info.plist deleted file mode 100644 index 71ebe57f..00000000 --- a/Passepartout/Launcher/Info.plist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - CFBundleShortVersionString - 2.3.6 - CFBundleVersion - 3630 - LSBackgroundOnly - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleName - $(PRODUCT_NAME) - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - com.algoritmico.Passepartout.config - - app_id - $(CFG_APP_ID) - - - diff --git a/Passepartout/Launcher/Launcher.entitlements b/Passepartout/Launcher/Launcher.entitlements deleted file mode 100644 index 852fa1a4..00000000 --- a/Passepartout/Launcher/Launcher.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/PassepartoutLibrary/.swiftpm/xcode/xcshareddata/xcschemes/OpenVPNAppExtension.xcscheme b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/AppLibrary.xcscheme similarity index 85% rename from PassepartoutLibrary/.swiftpm/xcode/xcshareddata/xcschemes/OpenVPNAppExtension.xcscheme rename to Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/AppLibrary.xcscheme index d4b422fe..62c4eb30 100644 --- a/PassepartoutLibrary/.swiftpm/xcode/xcshareddata/xcschemes/OpenVPNAppExtension.xcscheme +++ b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/AppLibrary.xcscheme @@ -1,6 +1,6 @@ @@ -49,9 +49,9 @@ diff --git a/Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutMac.xcscheme b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/AppLibraryTests.xcscheme similarity index 57% rename from Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutMac.xcscheme rename to Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/AppLibraryTests.xcscheme index cc966fb7..1b973eed 100644 --- a/Passepartout.xcodeproj/xcshareddata/xcschemes/PassepartoutMac.xcscheme +++ b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/AppLibraryTests.xcscheme @@ -1,33 +1,28 @@ + LastUpgradeVersion = "1540" + version = "1.7"> - - - - - - + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + + + + - - - - diff --git a/PassepartoutLibrary/.swiftpm/xcode/xcshareddata/xcschemes/WireGuardAppExtension.xcscheme b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/TunnelLibrary.xcscheme similarity index 84% rename from PassepartoutLibrary/.swiftpm/xcode/xcshareddata/xcschemes/WireGuardAppExtension.xcscheme rename to Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/TunnelLibrary.xcscheme index baf70190..eac7d868 100644 --- a/PassepartoutLibrary/.swiftpm/xcode/xcshareddata/xcschemes/WireGuardAppExtension.xcscheme +++ b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/TunnelLibrary.xcscheme @@ -1,6 +1,6 @@ @@ -49,9 +49,9 @@ diff --git a/Passepartout/Library/Package.resolved b/Passepartout/Library/Package.resolved new file mode 100644 index 00000000..d97cd9a2 --- /dev/null +++ b/Passepartout/Library/Package.resolved @@ -0,0 +1,77 @@ +{ + "pins" : [ + { + "identity" : "dtfoundation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Cocoanetics/DTFoundation.git", + "state" : { + "revision" : "a61be65dd7d5b2cde3acabd13bf320b71f2907a5", + "version" : "1.7.19" + } + }, + { + "identity" : "kvitto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Cocoanetics/Kvitto", + "state" : { + "revision" : "88888674d772ddcf19671159ed0022cb0bc37be2", + "version" : "1.0.6" + } + }, + { + "identity" : "openssl-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/openssl-apple", + "state" : { + "revision" : "0edc07c7a0e4ec2ca0f448dd68314241ccc925b3", + "version" : "3.2.107" + } + }, + { + "identity" : "passepartoutkit", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit", + "state" : { + "revision" : "2c32459d6a669e8feed0e6ea2d1250ef72364aa3", + "version" : "0.7.0" + } + }, + { + "identity" : "passepartoutkit-openvpn-openssl", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-openvpn-openssl", + "state" : { + "revision" : "a3092a6ee0a63f666aa47ef3f0f50c324a64598d", + "version" : "0.6.0" + } + }, + { + "identity" : "passepartoutkit-wireguard-go", + "kind" : "remoteSourceControl", + "location" : "git@github.com:passepartoutvpn/passepartoutkit-wireguard-go", + "state" : { + "revision" : "2cbd6023300d2dcc3f6f68de4812cf390421ec35", + "version" : "0.6.2" + } + }, + { + "identity" : "wg-go-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/wg-go-apple", + "state" : { + "revision" : "860e82efaf261da37483a5f51555be83e5a79ad3", + "version" : "0.0.20240714" + } + }, + { + "identity" : "wireguard-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/passepartoutvpn/wireguard-apple", + "state" : { + "revision" : "a896f784bc5ed94f29d97e376be5cfa08d4a5d44", + "version" : "1.1.1" + } + } + ], + "version" : 2 +} diff --git a/Passepartout/Library/Package.swift b/Passepartout/Library/Package.swift new file mode 100644 index 00000000..104d2f3f --- /dev/null +++ b/Passepartout/Library/Package.swift @@ -0,0 +1,97 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Library", + defaultLocalization: "en", + platforms: [ + .iOS(.v16), + .macOS(.v13), + .tvOS(.v17) + ], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "AppLibrary", + targets: [ + "AppLibrary", + "AppDataProfiles" + ] + ), + .library( + name: "TunnelLibrary", + targets: ["TunnelLibrary"] + ), + .library( + name: "UtilsLibrary", + targets: ["UtilsLibrary"] + ) + ], + dependencies: [ + .package(url: "git@github.com:passepartoutvpn/passepartoutkit", from: "0.7.0"), +// .package(url: "git@github.com:passepartoutvpn/passepartoutkit", revision: "d4f25ecfbcd00dbb6f08de18eda6e0cefbcc379d"), +// .package(path: "../../../passepartoutkit"), + .package(url: "git@github.com:passepartoutvpn/passepartoutkit-openvpn-openssl", from: "0.6.0"), +// .package(path: "../../../passepartoutkit-openvpn-openssl"), + .package(url: "git@github.com:passepartoutvpn/passepartoutkit-wireguard-go", from: "0.6.2"), +// .package(path: "../../../passepartoutkit-wireguard-go"), + .package(url: "https://github.com/Cocoanetics/Kvitto", from: "1.0.0") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "AppData", + dependencies: [ + .product(name: "PassepartoutKit", package: "passepartoutkit") + ] + ), + .target( + name: "AppDataProfiles", + dependencies: [ + "AppData", + "UtilsLibrary", + .product(name: "PassepartoutKit", package: "passepartoutkit") + ], + resources: [ + .process("Profiles.xcdatamodeld") + ] + ), + .target( + name: "AppLibrary", + dependencies: [ + "AppData", + "CommonLibrary", + "Kvitto" + ], + resources: [ + .process("Resources") + ] + ), + .target( + name: "CommonLibrary", + dependencies: [ + .product(name: "PassepartoutKit", package: "passepartoutkit"), + .product(name: "PassepartoutOpenVPNOpenSSL", package: "passepartoutkit-openvpn-openssl"), + .product(name: "PassepartoutWireGuardGo", package: "passepartoutkit-wireguard-go"), + "UtilsLibrary", + ], + resources: [ + .process("Resources") + ] + ), + .target( + name: "TunnelLibrary", + dependencies: ["CommonLibrary"] + ), + .target( + name: "UtilsLibrary" + ), + .testTarget( + name: "AppLibraryTests", + dependencies: ["AppLibrary"] + ) + ] +) diff --git a/Passepartout/App/Views/EndpointAdvancedView.swift b/Passepartout/Library/Sources/AppData/AppData.swift similarity index 89% rename from Passepartout/App/Views/EndpointAdvancedView.swift rename to Passepartout/Library/Sources/AppData/AppData.swift index ddcf1731..fb125567 100644 --- a/Passepartout/App/Views/EndpointAdvancedView.swift +++ b/Passepartout/Library/Sources/AppData/AppData.swift @@ -1,8 +1,8 @@ // -// EndpointAdvancedView.swift +// AppData.swift // Passepartout // -// Created by Davide De Rosa on 3/8/22. +// Created by Davide De Rosa on 8/11/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,5 +25,5 @@ import Foundation -enum EndpointAdvancedView { +public enum AppData { } diff --git a/Passepartout/Library/Sources/AppData/MockProfileRepository.swift b/Passepartout/Library/Sources/AppData/MockProfileRepository.swift new file mode 100644 index 00000000..7a621b0e --- /dev/null +++ b/Passepartout/Library/Sources/AppData/MockProfileRepository.swift @@ -0,0 +1,90 @@ +// +// MockProfileRepository.swift +// Passepartout +// +// Created by Davide De Rosa on 8/11/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Combine +import Foundation +import PassepartoutKit +import UtilsLibrary + +public final class MockProfileRepository: ProfileRepository { + private var profiles: [Profile] { + didSet { + profilesSubject.send(EntitiesResult(profiles, isFiltering: false)) + } + } + + private let profilesSubject: CurrentValueSubject, Never> + + public init(profiles: [Profile] = []) { + self.profiles = profiles + profilesSubject = CurrentValueSubject(EntitiesResult(profiles, isFiltering: false)) + } + + public var entitiesPublisher: AnyPublisher, Never> { + profilesSubject + .map { + EntitiesResult($0.entities.sorted { + $0.name < $1.name + }, isFiltering: $0.isFiltering) + } + .eraseToAnyPublisher() + } + + public func filter(byFormat format: String, arguments: [Any]?) { + print("Filter by format '\(format)' with \(arguments ?? [])") + guard let nameSearch = arguments?.first as? String, !nameSearch.isEmpty else { + profilesSubject.send(EntitiesResult(profiles, isFiltering: false)) + return + } + let match = nameSearch.lowercased() + let filtered = profiles.filter { + $0.name.lowercased().contains(match) + } + profilesSubject.send(EntitiesResult(filtered, isFiltering: true)) + } + + public func resetFilter() { + print("Reset filter") + profilesSubject.send(EntitiesResult(profiles, isFiltering: false)) + } + + public func saveEntities(_ entities: [Profile]) { + print("Save entities: \(entities.map(\.id))") + entities.forEach { profile in + if let index = profiles.firstIndex(where: { $0.id == profile.id }) { + profiles[index] = profile + } else { + profiles.append(profile) + } + } + } + + public func removeEntities(withIds ids: [UUID]) { + print("Remove entities: \(ids)") + profiles = profiles.filter { + !ids.contains($0.id) + } + } +} diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Domain/DebugLog.swift b/Passepartout/Library/Sources/AppData/ProfileRepository.swift similarity index 66% rename from PassepartoutLibrary/Sources/PassepartoutVPN/Domain/DebugLog.swift rename to Passepartout/Library/Sources/AppData/ProfileRepository.swift index 1cd8f8da..9fd43809 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Domain/DebugLog.swift +++ b/Passepartout/Library/Sources/AppData/ProfileRepository.swift @@ -1,8 +1,8 @@ // -// DebugLog.swift +// ProfileRepository.swift // Passepartout // -// Created by Davide De Rosa on 6/26/18. +// Created by Davide De Rosa on 8/11/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,15 +24,20 @@ // import Foundation +import PassepartoutKit +import UtilsLibrary -public struct DebugLog { - public let content: String - - public init(content: String) { - self.content = content - } - - public var contentData: Data? { - content.data(using: .utf8) +extension Profile: UniqueEntity { + public var uuid: UUID? { + id + } +} + +public protocol ProfileRepository: Repository where Entity == Profile { +} + +extension ProfileRepository { + public func filter(byName name: String) async throws { + try await filter(byFormat: "name CONTAINS[cd] %@", arguments: [name]) } } diff --git a/Passepartout/Mac/PassepartoutMac.swift b/Passepartout/Library/Sources/AppDataProfiles/AppData+Profiles.swift similarity index 70% rename from Passepartout/Mac/PassepartoutMac.swift rename to Passepartout/Library/Sources/AppDataProfiles/AppData+Profiles.swift index bfea0845..e80a2c7b 100644 --- a/Passepartout/Mac/PassepartoutMac.swift +++ b/Passepartout/Library/Sources/AppDataProfiles/AppData+Profiles.swift @@ -1,8 +1,8 @@ // -// PassepartoutMac.swift +// AppData+Profiles.swift // Passepartout // -// Created by Davide De Rosa on 6/19/22. +// Created by Davide De Rosa on 8/11/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,15 +23,17 @@ // along with Passepartout. If not, see . // +import AppData +import CoreData import Foundation -final class PassepartoutMac: NSObject, MacBridge { - required override init() { - super.init() - } - - let utils: MacUtils = DefaultMacUtils() +extension AppData { @MainActor - let menu: MacMenu = DefaultMacMenu() + public static let cdProfilesModel: NSManagedObjectModel = { + guard let model: NSManagedObjectModel = .mergedModel(from: [.module]) else { + fatalError("Unable to build Core Data model") + } + return model + }() } diff --git a/PassepartoutLibrary/Sources/PassepartoutProvidersImpl/Strategies/CDLocalProvidersRepository.swift b/Passepartout/Library/Sources/AppDataProfiles/CDProfile.swift similarity index 77% rename from PassepartoutLibrary/Sources/PassepartoutProvidersImpl/Strategies/CDLocalProvidersRepository.swift rename to Passepartout/Library/Sources/AppDataProfiles/CDProfile.swift index edee34b5..df79911f 100644 --- a/PassepartoutLibrary/Sources/PassepartoutProvidersImpl/Strategies/CDLocalProvidersRepository.swift +++ b/Passepartout/Library/Sources/AppDataProfiles/CDProfile.swift @@ -1,8 +1,8 @@ // -// CDLocalProvidersRepository.swift +// CDProfile.swift // Passepartout // -// Created by Davide De Rosa on 5/24/23. +// Created by Davide De Rosa on 8/11/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -26,10 +26,10 @@ import CoreData import Foundation -final class CDLocalProvidersRepository { - let context: NSManagedObjectContext - - init(_ context: NSManagedObjectContext) { - self.context = context - } +@objc(CDProfile) +final class CDProfile: NSManagedObject { + @NSManaged var uuid: UUID? + @NSManaged var name: String? + @NSManaged var encoded: String? + @NSManaged var lastUpdate: Date? } diff --git a/Passepartout/Library/Sources/AppDataProfiles/CDProfileRepository.swift b/Passepartout/Library/Sources/AppDataProfiles/CDProfileRepository.swift new file mode 100644 index 00000000..ee0d2272 --- /dev/null +++ b/Passepartout/Library/Sources/AppDataProfiles/CDProfileRepository.swift @@ -0,0 +1,70 @@ +// +// CDProfileRepository.swift +// Passepartout +// +// Created by Davide De Rosa on 8/11/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import AppData +import CoreData +import Foundation +import PassepartoutKit +import UtilsLibrary + +extension AppData { + public static func cdProfileRepository( + registry: Registry, + coder: ProfileCoder, + context: NSManagedObjectContext, + onResultError: ((Error) -> Bool)? + ) -> any ProfileRepository { + let repository = CoreDataRepository(context: context) { + $0.sortDescriptors = [ + .init(key: "name", ascending: true, selector: #selector(NSString.caseInsensitiveCompare)), + .init(key: "lastUpdate", ascending: true) + ] + } fromMapper: { + guard let encoded = $0.encoded else { + return nil + } + return try registry.decodedProfile(from: encoded, with: coder) + } toMapper: { + let encoded = try registry.encodedProfile($0, with: coder) + + let cdProfile = CDProfile(context: $1) + cdProfile.uuid = $0.id + cdProfile.name = $0.name + cdProfile.encoded = encoded + cdProfile.lastUpdate = Date() + return cdProfile + } onResultError: { + onResultError?($0) ?? true + } + + return repository + } +} + +extension CDProfile: CoreDataUniqueEntity { +} + +extension CoreDataRepository: ProfileRepository where T == Profile { +} diff --git a/PassepartoutLibrary/Sources/PassepartoutVPNImpl/Data/Profiles.xcdatamodeld/Model.xcdatamodel/contents b/Passepartout/Library/Sources/AppDataProfiles/Profiles.xcdatamodeld/Profiles.xcdatamodel/contents similarity index 51% rename from PassepartoutLibrary/Sources/PassepartoutVPNImpl/Data/Profiles.xcdatamodeld/Model.xcdatamodel/contents rename to Passepartout/Library/Sources/AppDataProfiles/Profiles.xcdatamodeld/Profiles.xcdatamodel/contents index d6c5819b..0256120b 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPNImpl/Data/Profiles.xcdatamodeld/Model.xcdatamodel/contents +++ b/Passepartout/Library/Sources/AppDataProfiles/Profiles.xcdatamodeld/Profiles.xcdatamodel/contents @@ -1,11 +1,9 @@ - + - - + - \ No newline at end of file diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Extensions/NetworkSettings+Extensions.swift b/Passepartout/Library/Sources/AppLibrary/AppLibrary.swift similarity index 50% rename from PassepartoutLibrary/Sources/PassepartoutVPN/Extensions/NetworkSettings+Extensions.swift rename to Passepartout/Library/Sources/AppLibrary/AppLibrary.swift index f3db4782..591dc57f 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Extensions/NetworkSettings+Extensions.swift +++ b/Passepartout/Library/Sources/AppLibrary/AppLibrary.swift @@ -1,8 +1,8 @@ // -// NetworkSettings+Extensions.swift +// AppLibrary.swift // Passepartout // -// Created by Davide De Rosa on 3/27/22. +// Created by Davide De Rosa on 7/31/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,41 +24,31 @@ // import Foundation +import PassepartoutKit -extension Profile.NetworkSettings { - public var isAutomaticGateway: Bool { - get { - gateway.choice == .automatic - } - set { - gateway.choice = newValue ? .automatic : .manual - } +public struct AppLibrary { + private init() { } - public var isAutomaticDNS: Bool { - get { - dns.choice == .automatic - } - set { - dns.choice = newValue ? .automatic : .manual - } - } - - public var isAutomaticProxy: Bool { - get { - proxy.choice == .automatic - } - set { - proxy.choice = newValue ? .automatic : .manual - } - } - - public var isAutomaticMTU: Bool { - get { - mtu.choice == .automatic - } - set { - mtu.choice = newValue ? .automatic : .manual + public static func configure(with context: AppContext) { + assertMissingModuleImplementations() + Task { + await context.iapManager.reloadReceipt() + try await context.tunnel.prepare() + } + } +} + +private extension AppLibrary { + static func assertMissingModuleImplementations() { + ModuleType.allCases.forEach { moduleType in + let module = moduleType.newModule() + guard module as? ModuleTypeProviding != nil else { + fatalError("\(moduleType): does not implement ModuleTypeProviding") + } + guard module as? any ModuleViewProviding != nil else { + fatalError("\(moduleType): does not implement ModuleViewProviding") + } } } } diff --git a/Passepartout/Library/Sources/AppLibrary/Business/AppContext.swift b/Passepartout/Library/Sources/AppLibrary/Business/AppContext.swift new file mode 100644 index 00000000..32cb150c --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Business/AppContext.swift @@ -0,0 +1,119 @@ +// +// AppContext.swift +// Passepartout +// +// Created by Davide De Rosa on 8/29/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Combine +import CommonLibrary +import Foundation +import PassepartoutKit +import UtilsLibrary + +@MainActor +public final class AppContext: ObservableObject { + public let iapManager: IAPManager + + public let profileManager: ProfileManager + + public let tunnel: Tunnel + + public let tunnelEnvironment: TunnelEnvironment + + public let connectionObserver: ConnectionObserver + + public let registry: Registry + + private let constants: Constants + + private var subscriptions: Set + + public init( + iapManager: IAPManager, + profileManager: ProfileManager, + tunnel: Tunnel, + tunnelEnvironment: TunnelEnvironment, + registry: Registry, + constants: Constants + ) { + self.iapManager = iapManager + self.profileManager = profileManager + self.tunnel = tunnel + self.tunnelEnvironment = tunnelEnvironment + self.registry = registry + self.constants = constants + subscriptions = [] + + connectionObserver = ConnectionObserver( + tunnel: tunnel, + environment: tunnelEnvironment, + interval: constants.connection.refreshInterval + ) + + observeObjects() + } +} + +private extension AppContext { + func observeObjects() { + profileManager + .didSave + .sink { [weak self] profile in + guard let self else { + return + } + guard profile.id == tunnel.installedProfile?.id else { + return + } + Task { + if profile.isInteractive { + try await self.tunnel.disconnect() + return + } + if self.tunnel.status == .active { + try await self.tunnel.reconnect(with: profile, processor: self.iapManager) + } else { + try await self.tunnel.reinstate(profile, processor: self.iapManager) + } + } + } + .store(in: &subscriptions) + + profileManager + .didUpdate + .sink { [weak self] _ in + guard let self else { + return + } + guard let installedProfile = tunnel.installedProfile else { + return + } + guard profileManager.exists(withId: installedProfile.id) else { + Task { + try await self.tunnel.disconnect() + } + return + } + } + .store(in: &subscriptions) + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Business/ConnectionObserver.swift b/Passepartout/Library/Sources/AppLibrary/Business/ConnectionObserver.swift new file mode 100644 index 00000000..27e68350 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Business/ConnectionObserver.swift @@ -0,0 +1,109 @@ +// +// ConnectionObserver.swift +// Passepartout +// +// Created by Davide De Rosa on 9/7/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Combine +import CommonLibrary +import Foundation +import PassepartoutKit + +@MainActor +public final class ConnectionObserver: ObservableObject { + let tunnel: Tunnel + + private let environment: TunnelEnvironment + + private let interval: TimeInterval + + public func value(forKey key: TunnelEnvironmentKey) -> T? where T: Decodable { + environment.environmentValue(forKey: key) + } + + public var connectionStatus: ConnectionStatus? { + value(forKey: TunnelEnvironmentKeys.connectionStatus) + } + + @Published + public private(set) var lastErrorCode: PassepartoutError.Code? { + didSet { + pp_log(.app, .info, "ConnectionObserver.lastErrorCode -> \(lastErrorCode?.rawValue ?? "nil")") + } + } + + @Published + public private(set) var dataCount: DataCount? + + private var subscriptions: Set + + public init( + tunnel: Tunnel, + environment: TunnelEnvironment, + interval: TimeInterval + ) { + self.tunnel = tunnel + self.environment = environment + self.interval = interval + subscriptions = [] + + observeObjects() + } +} + +private extension ConnectionObserver { + func observeObjects() { + tunnel + .$status + .receive(on: DispatchQueue.main) + .sink { [weak self] in + guard let self else { + return + } + switch $0 { + case .activating: + lastErrorCode = nil + + default: + lastErrorCode = value(forKey: TunnelEnvironmentKeys.lastErrorCode) + } + if $0 != .active { + dataCount = nil + } + } + .store(in: &subscriptions) + + Timer + .publish(every: interval, on: .main, in: .common) + .autoconnect() + .sink { [weak self] _ in + guard let self else { + return + } + guard tunnel.status == .active else { + return + } + dataCount = value(forKey: TunnelEnvironmentKeys.dataCount) + } + .store(in: &subscriptions) + } +} diff --git a/Passepartout/App/Reusable/Shortcut.swift b/Passepartout/Library/Sources/AppLibrary/Business/InteractiveManager.swift similarity index 55% rename from Passepartout/App/Reusable/Shortcut.swift rename to Passepartout/Library/Sources/AppLibrary/Business/InteractiveManager.swift index 8513521c..45185b57 100644 --- a/Passepartout/App/Reusable/Shortcut.swift +++ b/Passepartout/Library/Sources/AppLibrary/Business/InteractiveManager.swift @@ -1,8 +1,8 @@ // -// Shortcut.swift +// InteractiveManager.swift // Passepartout // -// Created by Davide De Rosa on 3/13/22. +// Created by Davide De Rosa on 9/9/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,35 +23,30 @@ // along with Passepartout. If not, see . // -#if !os(tvOS) import Foundation -import Intents +import PassepartoutKit -struct Shortcut: Identifiable, Hashable, Comparable { - let native: INVoiceShortcut +@MainActor +final class InteractiveManager: ObservableObject { + typealias CompletionBlock = (Profile) async throws -> Void - init(_ native: INVoiceShortcut) { - self.native = native + @Published + var isPresented = false + + @Published + private(set) var editor = ProfileEditor() + + private var onComplete: CompletionBlock? + + func present(with profile: Profile, onComplete: CompletionBlock?) { + editor = ProfileEditor(profile: profile) + self.onComplete = onComplete + isPresented = true } - var id: UUID { - native.identifier - } - - static func == (lhs: Self, rhs: Self) -> Bool { - lhs.phrase == rhs.phrase - } - - static func < (lhs: Self, rhs: Self) -> Bool { - lhs.phrase < rhs.phrase - } - - func hash(into hasher: inout Hasher) { - hasher.combine(phrase) - } - - private var phrase: String { - native.invocationPhrase.lowercased() + func complete() async throws { + isPresented = false + let newProfile = try editor.build() + try await onComplete?(newProfile) } } -#endif diff --git a/Passepartout/AppShared/Mac/Managers/LightUtils.swift b/Passepartout/Library/Sources/AppLibrary/Business/ModuleTypeProviding.swift similarity index 83% rename from Passepartout/AppShared/Mac/Managers/LightUtils.swift rename to Passepartout/Library/Sources/AppLibrary/Business/ModuleTypeProviding.swift index 66ec101d..6f6f7e2e 100644 --- a/Passepartout/AppShared/Mac/Managers/LightUtils.swift +++ b/Passepartout/Library/Sources/AppLibrary/Business/ModuleTypeProviding.swift @@ -1,8 +1,8 @@ // -// LightUtils.swift +// ModuleTypeProviding.swift // Passepartout // -// Created by Davide De Rosa on 7/16/22. +// Created by Davide De Rosa on 8/10/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,10 +24,8 @@ // import Foundation +import PassepartoutKit -@objc -public protocol LightUtils { - var launchesOnLogin: Bool { get set } - - func requestScene() +protocol ModuleTypeProviding { + var moduleType: ModuleType { get } } diff --git a/Passepartout/Library/Sources/AppLibrary/Business/ProfileEditor.swift b/Passepartout/Library/Sources/AppLibrary/Business/ProfileEditor.swift new file mode 100644 index 00000000..1b40c5fc --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Business/ProfileEditor.swift @@ -0,0 +1,274 @@ +// +// ProfileEditor.swift +// Passepartout +// +// Created by Davide De Rosa on 2/17/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Combine +import CommonLibrary +import Foundation +import PassepartoutKit + +@MainActor +final class ProfileEditor: ObservableObject { + private(set) var id: Profile.ID + + @Published + var name: String + + @Published + private(set) var modules: [any EditableModule] + + @Published + private(set) var activeModulesIds: Set + + @Published + private var moduleNames: [UUID: String] + + private(set) var removedModules: [UUID: any EditableModule] + + convenience init() { + self.init(modules: []) + } + + init(modules: [any EditableModule]) { + id = UUID() + name = "" + self.modules = modules + activeModulesIds = Set(modules.map(\.id)) + moduleNames = [:] + removedModules = [:] + } + + init(profile: Profile) { + id = profile.id + name = profile.name + modules = profile.modulesBuilders + activeModulesIds = profile.activeModulesIds + moduleNames = profile.moduleNames + removedModules = [:] + } + + func editProfile(_ profile: Profile) { + id = profile.id + name = profile.name + modules = profile.modulesBuilders + activeModulesIds = profile.activeModulesIds + moduleNames = profile.moduleNames + removedModules = [:] + } +} + +// MARK: - CRUD + +extension ProfileEditor { + var moduleTypes: [ModuleType] { + modules + .compactMap { + $0 as? ModuleTypeProviding + } + .map(\.moduleType) + } + + var availableModuleTypes: [ModuleType] { + ModuleType + .allCases + .filter { + // TODO: hide manual OpenVPN/WireGuard until editable + $0 != .openVPN && $0 != .wireGuard + } + .filter { + !moduleTypes.contains($0) + } + .sorted { + $0.localizedDescription < $1.localizedDescription + } + } + + func displayName(forModuleWithId moduleId: UUID) -> String? { + guard let name = moduleNames[moduleId] else { + return nil + } + let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) + return !trimmedName.isEmpty ? trimmedName : nil + } + + func name(forModuleWithId moduleId: UUID) -> String? { + moduleNames[moduleId] + } + + func setName(_ name: String, forModuleWithId moduleId: UUID) { + moduleNames[moduleId] = name + } + + func module(withId moduleId: UUID) -> (any EditableModule)? { + modules.first { + $0.id == moduleId + } ?? removedModules[moduleId] + } + + func moveModules(from offsets: IndexSet, to newOffset: Int) { + modules.move(fromOffsets: offsets, toOffset: newOffset) + } + + func removeModules(at offsets: IndexSet) { + offsets.forEach { + let module = modules[$0] + removedModules[module.id] = module + modules.remove(at: $0) + } + } + + func removeModule(withId moduleId: UUID) { + guard let index = modules.firstIndex(where: { $0.id == moduleId }) else { + return + } + let module = modules[index] + removedModules[module.id] = module + modules.remove(at: index) + } + + func saveModule(_ module: any EditableModule, activating: Bool) { + if let index = modules.firstIndex(where: { $0.id == module.id }) { + modules[index] = module + } else { + modules.append(module) + } + if activating { + activateModule(module) + } + } +} + +// MARK: - Active modules + +extension ProfileEditor { + func isActiveModule(withId moduleId: UUID) -> Bool { + activeModulesIds.contains(moduleId) && !removedModules.keys.contains(moduleId) + } + + var activeConnectionModule: (any EditableModule)? { + modules.first { + isActiveModule(withId: $0.id) && $0.buildsConnectionModule + } + } + + var activeModules: [any EditableModule] { + modules.filter { + activeModulesIds.contains($0.id) + } + } + + func activateModule(_ module: any EditableModule) { + activeModulesIds.insert(module.id) + } + + func toggleModule(withId moduleId: UUID) { + guard let existingModule = module(withId: moduleId) else { + return + } + if isActiveModule(withId: moduleId) { + activeModulesIds.remove(moduleId) + } else { + activateModule(existingModule) + } + } +} + +private extension ProfileEditor { + func checkConstraints() throws { + if activeConnectionModule == nil, + let ipModule = modules.first(where: { activeModulesIds.contains($0.id) && $0 is IPModule.Builder }) { + throw AppError.ipModuleRequiresConnection(ipModule) + } + + let connectionModules = modules.filter { + activeModulesIds.contains($0.id) && $0.buildsConnectionModule + } + guard connectionModules.count <= 1 else { + throw AppError.multipleConnectionModules(connectionModules) + } + } +} + +// MARK: - Building + +extension ProfileEditor { + func build() throws -> Profile { + try checkConstraints() + + var builder = Profile.Builder(id: id) + let trimmedName = name.trimmingCharacters(in: .whitespaces) + guard !trimmedName.isEmpty else { + throw AppError.emptyProfileName + } + builder.name = trimmedName + builder.modules = try modules.compactMap { + do { + return try $0.tryBuild() + } catch { + throw AppError.malformedModule($0, error: error) + } + } + builder.activeModulesIds = activeModulesIds + builder.moduleNames = moduleNames.reduce(into: [:]) { + let trimmedName = $1.value.trimmingCharacters(in: .whitespaces) + guard !trimmedName.isEmpty else { + return + } + $0[$1.key] = trimmedName + } + let profile = try builder.tryBuild() + + // update local view + modules = profile.modulesBuilders + removedModules.removeAll() + + return profile + } +} + +private extension Profile { + var modulesBuilders: [any EditableModule] { + modules.compactMap { + guard let buildableModule = $0 as? any BuildableType else { + return nil + } + let builder = buildableModule.builder() as any BuilderType + return builder as? any EditableModule + } + } +} + +// MARK: - Saving + +extension ProfileEditor { + func save(to profileManager: ProfileManager) async throws { + do { + let newProfile = try build() + try await profileManager.save(newProfile) + } catch { + pp_log(.app, .fault, "Unable to save edited profile: \(error)") + throw error + } + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Business/ProfileImporter.swift b/Passepartout/Library/Sources/AppLibrary/Business/ProfileImporter.swift new file mode 100644 index 00000000..17bdd1c8 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Business/ProfileImporter.swift @@ -0,0 +1,135 @@ +// +// ProfileImporter.swift +// Passepartout +// +// Created by Davide De Rosa on 9/8/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit +import UtilsLibrary + +@MainActor +final class ProfileImporter: ObservableObject { + + @Published + var isPresentingPassphrase = false + + @Published + var currentPassphrase = "" + + private(set) var urlsRequiringPassphrase: [URL] = [] + + var nextURL: URL? { + urlsRequiringPassphrase.first + } + + func tryImport( + urls: [URL], + profileManager: ProfileManager, + registry: Registry + ) async throws { + var withPassphrase: [URL] = [] + + for url in urls { + do { + try await importURL( + url, + withPassphrase: nil, + profileManager: profileManager, + registry: registry + ) + } catch { + if let error = error as? PassepartoutError, + error.code == .OpenVPN.passphraseRequired { + withPassphrase.append(url) + continue + } + pp_log(.app, .fault, "Unable to import URL: \(error)") + throw error + } + } + + urlsRequiringPassphrase = withPassphrase + if !urlsRequiringPassphrase.isEmpty { + scheduleNextImport() + } + } + + func reImport(url: URL, profileManager: ProfileManager, registry: Registry) async throws { + do { + try await importURL( + url, + withPassphrase: currentPassphrase, + profileManager: profileManager, + registry: registry + ) + urlsRequiringPassphrase.removeFirst() + scheduleNextImport() + } catch { + scheduleNextImport() + throw error + } + } + + func cancelImport() { + urlsRequiringPassphrase.removeFirst() + scheduleNextImport() + } +} + +private extension ProfileImporter { + func scheduleNextImport() { + guard !urlsRequiringPassphrase.isEmpty else { + return + } + Task { + // XXX: re-present same alert after artificial delay + try? await Task.sleep(for: .milliseconds(500)) + currentPassphrase = "" + isPresentingPassphrase = true + } + } + + func importURL( + _ url: URL, + withPassphrase passphrase: String?, + profileManager: ProfileManager, + registry: Registry + ) async throws { + guard url.startAccessingSecurityScopedResource() else { + throw AppError.permissionDenied + } + defer { + url.stopAccessingSecurityScopedResource() + } + + let module = try registry.module(fromURL: url, object: passphrase) + + var builder = Profile.Builder() + builder.name = url.lastPathComponent + builder.modules = [module] + builder.activeModulesIds = [module.id] + let profile = try builder.tryBuild() + + try await profileManager.save(profile) + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Business/ProfileManager.swift b/Passepartout/Library/Sources/AppLibrary/Business/ProfileManager.swift new file mode 100644 index 00000000..d5d3338d --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Business/ProfileManager.swift @@ -0,0 +1,193 @@ +// +// ProfileManager.swift +// Passepartout +// +// Created by Davide De Rosa on 2/19/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import AppData +import Combine +import Foundation +import PassepartoutKit + +@MainActor +public final class ProfileManager: ObservableObject { + public let didSave: PassthroughSubject + + public var didUpdate: AnyPublisher<[Profile], Never> { + $profiles.eraseToAnyPublisher() + } + + @Published + var profiles: [Profile] + + private var allProfileIds: Set + + private let repository: any ProfileRepository + + private let searchSubject: CurrentValueSubject + + private var subscriptions: Set + + // for testing/previews + init(profiles: [Profile]) { + didSave = PassthroughSubject() + self.profiles = profiles.sorted { + $0.name.lowercased() < $1.name.lowercased() + } + allProfileIds = [] + repository = MockProfileRepository(profiles: profiles) + searchSubject = CurrentValueSubject("") + subscriptions = [] + + observeObjects(searchDebounce: 0) + } + + public init(repository: any ProfileRepository, searchDebounce: Int = 200) { + didSave = PassthroughSubject() + profiles = [] + allProfileIds = [] + self.repository = repository + searchSubject = CurrentValueSubject("") + subscriptions = [] + + observeObjects(searchDebounce: searchDebounce) + } + + public var hasProfiles: Bool { + !profiles.isEmpty + } + + public var isSearching: Bool { + !searchSubject.value.isEmpty + } + + public var headers: [ProfileHeader] { + profiles.map { + $0.header() + } + } + + public func search(byName name: String) { + searchSubject.send(name) + } + + public func profile(withId profileId: Profile.ID) -> Profile? { + profiles.first { + $0.id == profileId + } + } + + public func save(_ profile: Profile) async throws { + do { + try await repository.saveEntities([profile]) + didSave.send(profile) + } catch { + pp_log(.app, .fault, "Unable to save profile \(profile.id): \(error)") + throw error + } + } + + public func remove(withId profileId: Profile.ID) async { + await remove(withIds: [profileId]) + } + + public func remove(withIds profileIds: [Profile.ID]) async { + do { + allProfileIds.subtract(profileIds) + try await repository.removeEntities(withIds: profileIds) + } catch { + pp_log(.app, .fault, "Unable to remove profiles \(profileIds): \(error)") + } + } + + public func exists(withId profileId: Profile.ID) -> Bool { + allProfileIds.contains(profileId) + } +} + +extension ProfileManager { + public func new(withName name: String) -> Profile { + var builder = Profile.Builder() + builder.name = firstUniqueName(from: name) + do { + return try builder.tryBuild() + } catch { + fatalError("Unable to build new empty profile: \(error)") + } + } + + public func duplicate(profileWithId profileId: Profile.ID) async throws { + guard let profile = profile(withId: profileId) else { + return + } + + var builder = profile.builder(withNewId: true) + builder.name = firstUniqueName(from: profile.name) + let copy = try builder.tryBuild() + + try await save(copy) + } +} + +private extension ProfileManager { + func observeObjects(searchDebounce: Int) { + repository + .entitiesPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] in + guard let self else { + return + } + self.profiles = $0.entities + if !$0.isFiltering { + allProfileIds = Set($0.entities.map(\.id)) + } + } + .store(in: &subscriptions) + + searchSubject + .debounce(for: .milliseconds(searchDebounce), scheduler: DispatchQueue.main) + .sink { [weak self] search in + Task { + guard !search.isEmpty else { + try await self?.repository.resetFilter() + return + } + try await self?.repository.filter(byName: search) + } + } + .store(in: &subscriptions) + } + + func firstUniqueName(from name: String) -> String { + let allNames = profiles.map(\.name) + var newName = name + var index = 1 + while true { + if !allNames.contains(newName) { + return newName + } + newName = [name, index.description].joined(separator: ".") + index += 1 + } + } +} diff --git a/Passepartout/Mac/Reusable/StaticSystemMenu.swift b/Passepartout/Library/Sources/AppLibrary/Business/ProfileManagerProviding.swift similarity index 62% rename from Passepartout/Mac/Reusable/StaticSystemMenu.swift rename to Passepartout/Library/Sources/AppLibrary/Business/ProfileManagerProviding.swift index a1616098..4acc3985 100644 --- a/Passepartout/Mac/Reusable/StaticSystemMenu.swift +++ b/Passepartout/Library/Sources/AppLibrary/Business/ProfileManagerProviding.swift @@ -1,8 +1,8 @@ // -// StaticSystemMenu.swift +// ProfileManagerProviding.swift // Passepartout // -// Created by Davide De Rosa on 7/3/22. +// Created by Davide De Rosa on 9/3/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,25 +23,25 @@ // along with Passepartout. If not, see . // -import AppKit import Foundation +import PassepartoutKit -struct StaticSystemMenu: SystemMenu { - let children: [ItemGroup] +protocol ProfileManagerProviding { + var profileManager: ProfileManager { get } +} - init(_ children: [ItemGroup]) { - self.children = children - } - - var asMenu: NSMenu { - let menu = NSMenu() - if !children.isEmpty { - children.forEach { - $0.asMenuItems(withParent: menu).forEach { - menu.addItem($0) - } +@MainActor +extension ProfileManagerProviding { + func removeProfiles(at offsets: IndexSet) { + let idsToRemove = profileManager.headers + .enumerated() + .filter { + offsets.contains($0.offset) } + .map(\.element.id) + + Task { + await profileManager.remove(withIds: idsToRemove) } - return menu } } diff --git a/Passepartout/Library/Sources/AppLibrary/Business/Tunnel+Extensions.swift b/Passepartout/Library/Sources/AppLibrary/Business/Tunnel+Extensions.swift new file mode 100644 index 00000000..3254f1df --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Business/Tunnel+Extensions.swift @@ -0,0 +1,79 @@ +// +// Tunnel+Extensions.swift +// Passepartout +// +// Created by Davide De Rosa on 8/11/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import Foundation +import PassepartoutKit + +protocol ProfileProcessor { + func processedProfile(_ profile: Profile) throws -> Profile +} + +extension Tunnel { + func reinstate(_ profile: Profile, processor: ProfileProcessor) async throws { + try await install(profile, processor: processor) + } + + func connect(with profile: Profile, processor: ProfileProcessor) async throws { + try await install(profile, processor: processor) + guard !Task.isCancelled else { + return + } + try await connect() + } + + func reconnect(with profile: Profile, processor: ProfileProcessor) async throws { + try await disconnect() + guard !Task.isCancelled else { + return + } + try await install(profile, processor: processor) + guard !Task.isCancelled else { + return + } + try await connect() + } + + func currentLog(parameters: Constants.Log) async -> [String] { + let output = try? await sendMessage(.localLog( + sinceLast: parameters.sinceLast, + maxLevel: parameters.maxLevel + )) + switch output { + case .debugLog(let log): + return log.lines.map(parameters.formatter.formattedLine) + + default: + return [] + } + } +} + +private extension Tunnel { + func install(_ profile: Profile, processor: ProfileProcessor) async throws { + let newProfile = try processor.processedProfile(profile) + try await install(profile: newProfile, title: \.name) + } +} diff --git a/Passepartout/Mac/Views/VisibilityItem+ViewModel.swift b/Passepartout/Library/Sources/AppLibrary/Business/TunnelContextProviding.swift similarity index 59% rename from Passepartout/Mac/Views/VisibilityItem+ViewModel.swift rename to Passepartout/Library/Sources/AppLibrary/Business/TunnelContextProviding.swift index 19043a27..9c64282b 100644 --- a/Passepartout/Mac/Views/VisibilityItem+ViewModel.swift +++ b/Passepartout/Library/Sources/AppLibrary/Business/TunnelContextProviding.swift @@ -1,8 +1,8 @@ // -// VisibilityItem+ViewModel.swift +// TunnelContextProviding.swift // Passepartout // -// Created by Davide De Rosa on 7/3/22. +// Created by Davide De Rosa on 9/5/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,28 +23,26 @@ // along with Passepartout. If not, see . // -import AppKit import Foundation +import PassepartoutKit -extension VisibilityItem { +protocol TunnelContextProviding { + var tunnel: Tunnel { get } - @MainActor - final class ViewModel { - private let transformer: ObservableProcessTransformer + var connectionObserver: ConnectionObserver { get } +} - private let utils: LightUtils - - init(utils: LightUtils) { - transformer = .shared - self.utils = utils - } - - @objc func bringToForeground() { - transformer.bringToForeground() - if transformer.isForeground { - utils.requestScene() - NSApp.activate(ignoringOtherApps: true) +@MainActor +extension TunnelContextProviding { + var tunnelConnectionStatus: TunnelStatus { + var status = tunnel.status + if status == .active, let connectionStatus = connectionObserver.connectionStatus { + if connectionStatus == .connected { + status = .active + } else { + status = .activating } } + return status } } diff --git a/PassepartoutLibrary/Sources/PassepartoutProviders/Domain/ProviderLocation.swift b/Passepartout/Library/Sources/AppLibrary/Business/TunnelInstallationProviding.swift similarity index 50% rename from PassepartoutLibrary/Sources/PassepartoutProviders/Domain/ProviderLocation.swift rename to Passepartout/Library/Sources/AppLibrary/Business/TunnelInstallationProviding.swift index 2b1c0e89..245d6e43 100644 --- a/PassepartoutLibrary/Sources/PassepartoutProviders/Domain/ProviderLocation.swift +++ b/Passepartout/Library/Sources/AppLibrary/Business/TunnelInstallationProviding.swift @@ -1,8 +1,8 @@ // -// ProviderLocation.swift +// TunnelInstallationProviding.swift // Passepartout // -// Created by Davide De Rosa on 3/15/22. +// Created by Davide De Rosa on 9/3/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,31 +24,38 @@ // import Foundation -import PassepartoutCore +import PassepartoutKit -public struct ProviderLocation { - public let providerMetadata: ProviderMetadata +protocol TunnelInstallationProviding { + var profileManager: ProfileManager { get } - public let vpnProtocol: VPNProtocolType + var tunnel: Tunnel { get } +} - public let categoryName: String +struct TunnelInstallation { + let header: ProfileHeader - public let countryCode: String + let isEnabled: Bool +} - public var onlyServer: ProviderServer? { - guard servers?.count == 1 else { +@MainActor +extension TunnelInstallationProviding { + var installation: TunnelInstallation? { + guard let installedProfile = tunnel.installedProfile else { return nil } - return servers?.first + guard let header = profileManager.headers.first(where: { + $0.id == installedProfile.id + }) else { + return nil + } + return TunnelInstallation(header: header, isEnabled: installedProfile.isEnabled) } - public let servers: [ProviderServer]? - - public init(providerMetadata: ProviderMetadata, vpnProtocol: VPNProtocolType, categoryName: String, countryCode: String, servers: [ProviderServer]?) { - self.providerMetadata = providerMetadata - self.vpnProtocol = vpnProtocol - self.categoryName = categoryName - self.countryCode = countryCode - self.servers = servers + var installedProfile: Profile? { + guard let id = tunnel.installedProfile?.id else { + return nil + } + return profileManager.profile(withId: id) } } diff --git a/PassepartoutLibrary/Sources/PassepartoutCore/Reusable/GenericWebResponse.swift b/Passepartout/Library/Sources/AppLibrary/Domain/AppError.swift similarity index 62% rename from PassepartoutLibrary/Sources/PassepartoutCore/Reusable/GenericWebResponse.swift rename to Passepartout/Library/Sources/AppLibrary/Domain/AppError.swift index cd4d7100..7e1cc7fe 100644 --- a/PassepartoutLibrary/Sources/PassepartoutCore/Reusable/GenericWebResponse.swift +++ b/Passepartout/Library/Sources/AppLibrary/Domain/AppError.swift @@ -1,8 +1,8 @@ // -// GenericWebResponse.swift +// AppError.swift // Passepartout // -// Created by Davide De Rosa on 11/20/19. +// Created by Davide De Rosa on 8/27/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,22 +24,26 @@ // import Foundation +import PassepartoutKit -public struct GenericWebResponse { - public let value: T? +enum AppError { + case emptyProfileName - public let lastModifiedString: String? + case malformedModule(any EditableModule, error: Error) - public var lastModified: Date? { - guard let string = lastModifiedString else { - return nil + case multipleConnectionModules([any EditableModule]) + + case ipModuleRequiresConnection(any EditableModule) + + case permissionDenied + + case generic(PassepartoutError) + + init(_ error: Error) { + if let spError = error as? AppError { + self = spError + } else { + self = .generic(PassepartoutError(error)) } - return GenericWebParser.lastModifiedDate(string: string) - } - - public let isCached: Bool - - public static func empty() -> GenericWebResponse { - GenericWebResponse(value: nil, lastModifiedString: nil, isCached: false) } } diff --git a/Passepartout/Library/Sources/AppLibrary/Domain/Issue+App.swift b/Passepartout/Library/Sources/AppLibrary/Domain/Issue+App.swift new file mode 100644 index 00000000..e539f3b4 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Domain/Issue+App.swift @@ -0,0 +1,70 @@ +// +// Issue+App.swift +// Passepartout +// +// Created by Davide De Rosa on 9/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import Foundation +import PassepartoutKit + +extension Issue { + static func fromBundle(_ bundle: BundleConfiguration, purchasedProducts: Set, tunnel: Tunnel) async -> Self { + let appLog = CommonLibrary.currentLog(parameters: Constants.shared.log) + .joined(separator: "\n") + .data(using: .utf8) + + let tunnelLog: Data? + + // live tunnel log + if await tunnel.status != .inactive { + tunnelLog = await tunnel.currentLog(parameters: Constants.shared.log) + .joined(separator: "\n") + .data(using: .utf8) + } + // latest persisted tunnel log + else if let latestTunnelEntry = CommonLibrary.availableLogs(at: Constants.shared.urlForTunnelLog) + .max(by: { $0.key < $1.key }) { + + tunnelLog = try? Data(contentsOf: latestTunnelEntry.value) + } + // nothing + else { + tunnelLog = nil + } + + return Issue( + appLine: "\(Strings.Unlocalized.appName) \(bundle.versionString)", + purchasedProducts: purchasedProducts, + appLog: appLog, + tunnelLog: tunnelLog + ) + } + + var to: String { + Constants.shared.emails.issues + } + + var subject: String { + Strings.Unlocalized.Issues.subject + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Domain/Issue.swift b/Passepartout/Library/Sources/AppLibrary/Domain/Issue.swift new file mode 100644 index 00000000..a6cd2daf --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Domain/Issue.swift @@ -0,0 +1,102 @@ +// +// Issue.swift +// Passepartout +// +// Created by Davide De Rosa on 9/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) +import CommonLibrary +import Foundation +import UIKit +#else +import AppKit +import CommonLibrary +import Foundation +#endif + +struct Issue: Identifiable { + let id: UUID + + let appLine: String? + + let purchasedProducts: Set + + let appLog: Data? + + let tunnelLog: Data? + + let osLine: String + + let deviceLine: String? + + init(appLine: String?, purchasedProducts: Set, appLog: Data? = nil, tunnelLog: Data? = nil) { + id = UUID() + self.appLine = appLine + self.purchasedProducts = purchasedProducts + self.appLog = appLog + self.tunnelLog = tunnelLog + + let osName: String + let osVersion: String + let deviceType: String? + // providerName / providerLastUpdate + +#if os(iOS) + let device: UIDevice = .current + osName = device.systemName + osVersion = device.systemVersion + deviceType = device.model +#else + let os = ProcessInfo().operatingSystemVersion + osName = "macOS" + osVersion = "\(os.majorVersion).\(os.minorVersion).\(os.patchVersion)" + deviceType = nil +#endif + + osLine = "\(osName) \(osVersion)" + deviceLine = deviceType + } + + var body: String { + template + .replacingOccurrences(of: "$appLine", with: appLine ?? "unknown") + .replacingOccurrences(of: "$osLine", with: osLine) + .replacingOccurrences(of: "$deviceLine", with: deviceLine ?? "unknown") + // TODO: replace with provider later + .replacingOccurrences(of: "$providerName", with: "none") + .replacingOccurrences(of: "$providerLastUpdate", with: "unknown") + .replacingOccurrences(of: "$purchasedProducts", with: purchasedProducts.map(\.rawValue).description) + } +} + +private extension Issue { + var template: String { + do { + guard let templateURL = Bundle.module.url(forResource: "Issue", withExtension: "txt") else { + fatalError("Unable to find Issue.txt in Resources") + } + return try String(contentsOf: templateURL) + } catch { + fatalError("Unable to parse Issue.txt: \(error)") + } + } +} diff --git a/PassepartoutLibrary/Sources/PassepartoutProviders/Strategies/RemoteProvidersStrategy.swift b/Passepartout/Library/Sources/AppLibrary/Domain/ModuleType+New.swift similarity index 59% rename from PassepartoutLibrary/Sources/PassepartoutProviders/Strategies/RemoteProvidersStrategy.swift rename to Passepartout/Library/Sources/AppLibrary/Domain/ModuleType+New.swift index ac459b19..31fa41cd 100644 --- a/PassepartoutLibrary/Sources/PassepartoutProviders/Strategies/RemoteProvidersStrategy.swift +++ b/Passepartout/Library/Sources/AppLibrary/Domain/ModuleType+New.swift @@ -1,8 +1,8 @@ // -// RemoteProvidersStrategy.swift +// ModuleType+New.swift // Passepartout // -// Created by Davide De Rosa on 5/24/23. +// Created by Davide De Rosa on 9/6/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,29 +23,29 @@ // along with Passepartout. If not, see . // -import Combine import Foundation -import PassepartoutCore +import PassepartoutKit -public enum RemoteProvidersPriority { - case bundle +extension ModuleType { + func newModule() -> any EditableModule { + switch self { + case .openVPN: + return OpenVPNModule.Builder() - case remote + case .wireGuard: + return WireGuardModule.Builder(configurationBuilder: .default) - case remoteThenBundle -} - -public protocol RemoteProvidersStrategy { - func saveIndex( - priority: RemoteProvidersPriority, - onFetch: @escaping () -> Void - ) -> AnyPublisher - - func saveProvider( - withName providerName: ProviderName, - vpnProtocol: VPNProtocolType, - lastUpdate: Date?, - priority: RemoteProvidersPriority, - onFetch: @escaping () -> Void - ) -> AnyPublisher + case .dns: + return DNSModule.Builder() + + case .httpProxy: + return HTTPProxyModule.Builder() + + case .ip: + return IPModule.Builder() + + case .onDemand: + return OnDemandModule.Builder() + } + } } diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Extensions/Domain+Loggable.swift b/Passepartout/Library/Sources/AppLibrary/Domain/ModuleType.swift similarity index 71% rename from PassepartoutLibrary/Sources/PassepartoutVPN/Extensions/Domain+Loggable.swift rename to Passepartout/Library/Sources/AppLibrary/Domain/ModuleType.swift index 2163e163..5d59bbaf 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Extensions/Domain+Loggable.swift +++ b/Passepartout/Library/Sources/AppLibrary/Domain/ModuleType.swift @@ -1,8 +1,8 @@ // -// Domain+Loggable.swift +// ModuleType.swift // Passepartout // -// Created by Davide De Rosa on 4/7/22. +// Created by Davide De Rosa on 2/19/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,16 +24,25 @@ // import Foundation -import PassepartoutCore +import PassepartoutKit +import PassepartoutWireGuardGo -extension Profile.Header: Loggable { - public var logDescription: String { - "{\(id), '\(name)'}" - } +enum ModuleType: String, CaseIterable { + case openVPN + + case wireGuard + + case dns + + case httpProxy + + case ip + + case onDemand } -extension Profile: Loggable { - public var logDescription: String { - header.logDescription +extension ModuleType: Identifiable { + var id: String { + rawValue } } diff --git a/Passepartout/App/Extensions/DebugLog+Constants.swift b/Passepartout/Library/Sources/AppLibrary/Domain/Modules+ModuleTypeProviding.swift similarity index 54% rename from Passepartout/App/Extensions/DebugLog+Constants.swift rename to Passepartout/Library/Sources/AppLibrary/Domain/Modules+ModuleTypeProviding.swift index 5bf8d8e6..3e773ba2 100644 --- a/Passepartout/App/Extensions/DebugLog+Constants.swift +++ b/Passepartout/Library/Sources/AppLibrary/Domain/Modules+ModuleTypeProviding.swift @@ -1,8 +1,8 @@ // -// DebugLog+Constants.swift +// DescriptiveModule+L10n.swift // Passepartout // -// Created by Davide De Rosa on 3/24/22. +// Created by Davide De Rosa on 8/10/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,22 +24,40 @@ // import Foundation -import PassepartoutLibrary +import PassepartoutKit -extension DebugLog { - static func decoratedMetadataString() -> String { - decoratedMetadataString(Constants.Global.appName, Constants.Global.appVersionString) - } - - static func decoratedMetadataData() -> Data { - decoratedMetadataData(Constants.Global.appName, Constants.Global.appVersionString) - } - - func decoratedString() -> String { - decoratedString(Constants.Global.appName, Constants.Global.appVersionString) - } - - func decoratedData() -> Data { - decoratedData(Constants.Global.appName, Constants.Global.appVersionString) +extension OpenVPNModule.Builder: ModuleTypeProviding { + var moduleType: ModuleType { + .openVPN + } +} + +extension WireGuardModule.Builder: ModuleTypeProviding { + var moduleType: ModuleType { + .wireGuard + } +} + +extension DNSModule.Builder: ModuleTypeProviding { + var moduleType: ModuleType { + .dns + } +} + +extension HTTPProxyModule.Builder: ModuleTypeProviding { + var moduleType: ModuleType { + .httpProxy + } +} + +extension IPModule.Builder: ModuleTypeProviding { + var moduleType: ModuleType { + .ip + } +} + +extension OnDemandModule.Builder: ModuleTypeProviding { + var moduleType: ModuleType { + .onDemand } } diff --git a/PassepartoutLibrary/Sources/PassepartoutCore/Reusable/ValueHolder.swift b/Passepartout/Library/Sources/AppLibrary/Domain/PaywallReason.swift similarity index 84% rename from PassepartoutLibrary/Sources/PassepartoutCore/Reusable/ValueHolder.swift rename to Passepartout/Library/Sources/AppLibrary/Domain/PaywallReason.swift index e9463405..05855153 100644 --- a/PassepartoutLibrary/Sources/PassepartoutCore/Reusable/ValueHolder.swift +++ b/Passepartout/Library/Sources/AppLibrary/Domain/PaywallReason.swift @@ -1,8 +1,8 @@ // -// ValueHolder.swift +// PaywallReason.swift // Passepartout // -// Created by Davide De Rosa on 4/8/22. +// Created by Davide De Rosa on 9/14/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,8 +25,8 @@ import Foundation -public protocol ValueHolder { - associatedtype ValueType +public enum PaywallReason: Hashable { + case restricted - var value: ValueType { get } + case purchase(AppFeature) } diff --git a/PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSProviderCategory.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppFeature.swift similarity index 63% rename from PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSProviderCategory.swift rename to Passepartout/Library/Sources/AppLibrary/IAP/AppFeature.swift index 4940be78..edc5476e 100644 --- a/PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSProviderCategory.swift +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppFeature.swift @@ -1,8 +1,8 @@ // -// WSProviderCategory.swift +// AppFeature.swift // Passepartout // -// Created by Davide De Rosa on 4/11/19. +// Created by Davide De Rosa on 9/10/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,23 +25,31 @@ import Foundation -public struct WSProviderCategory: Codable { - enum CodingKeys: String, CodingKey { - case name +public enum AppFeature: String, CaseIterable { + case appleTV - case locations + case networkSettings - case supportedPresetIds = "presets" - } + case onDemand - public let name: String + case providers - public let locations: [WSProviderLocation] + case siri - public var supportedPresetIds: [String]? + public static let allCases: [AppFeature] = [ + .networkSettings, + .onDemand, + .providers, + .siri + ] +} - public init(name: String, locations: [WSProviderLocation]) { - self.name = name - self.locations = locations +extension AppFeature: Identifiable { + public var id: String { + rawValue } } + +protocol AppFeatureProviding { + var features: [AppFeature] { get } +} diff --git a/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Donations.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Donations.swift new file mode 100644 index 00000000..a8b3f92c --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Donations.swift @@ -0,0 +1,59 @@ +// +// AppProduct+Donations.swift +// Passepartout +// +// Created by Davide De Rosa on 9/10/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation + +extension AppProduct { + public enum Donations { + public static let tiny = AppProduct(donationId: "Tiny") + + public static let small = AppProduct(donationId: "Small") + + public static let medium = AppProduct(donationId: "Medium") + + public static let big = AppProduct(donationId: "Big") + + public static let huge = AppProduct(donationId: "Huge") + + public static let maxi = AppProduct(donationId: "Maxi") + + static let all: [AppProduct] = [ + .Donations.tiny, + .Donations.small, + .Donations.medium, + .Donations.big, + .Donations.huge, + .Donations.maxi + ] + } + + private init(donationId: String) { + self.init(rawValue: "donations.\(donationId)")! + } + + var isDonation: Bool { + rawValue.hasPrefix("donations") + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Features.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Features.swift new file mode 100644 index 00000000..32a3332c --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Features.swift @@ -0,0 +1,113 @@ +// +// AppProduct+Features.swift +// Passepartout +// +// Created by Davide De Rosa on 9/10/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation + +extension AppProduct { + public enum Features { + public static let allProviders = AppProduct(featureId: "all_providers") + + public static let appleTV = AppProduct(featureId: "appletv") + + public static let networkSettings = AppProduct(featureId: "network_settings") + + public static let siriShortcuts = AppProduct(featureId: "siri") + + public static let trustedNetworks = AppProduct(featureId: "trusted_networks") + + static let all: [AppProduct] = [ + .Features.allProviders, + .Features.appleTV, + .Features.networkSettings, + .Features.siriShortcuts, + .Features.trustedNetworks + ] + } + + public enum Full { + public static let iOS = AppProduct(featureId: "full_version") + + public static let macOS = AppProduct(featureId: "full_mac_version") + + public static let allPlatforms = AppProduct(featureId: "full_multi_version") + + static let all: [AppProduct] = [ + .Full.iOS, + .Full.macOS, + .Full.allPlatforms + ] + } + + static let featurePrefix = "features." + + private init(featureId: String) { + self.init(rawValue: "\(Self.featurePrefix)\(featureId)")! + } + + var isFeature: Bool { + rawValue.hasPrefix(Self.featurePrefix) + } +} + +extension AppProduct: AppFeatureProviding { + var features: [AppFeature] { + switch self { + case .Features.allProviders: + return [.providers] + + case .Features.appleTV: + return [.appleTV] + + case .Features.networkSettings: + return [.networkSettings] + + case .Features.siriShortcuts: + return [.siri] + + case .Features.trustedNetworks: + return [.onDemand] + + case .Full.allPlatforms: + return AppFeature.allCases + + case .Full.iOS: +#if os(iOS) + return AppFeature.allCases +#else + return [] +#endif + + case .Full.macOS: +#if os(macOS) + return AppFeature.allCases +#else + return [] +#endif + + default: + return [] + } + } +} diff --git a/Passepartout/Mac/Reusable/SystemMenu.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Providers.swift similarity index 85% rename from Passepartout/Mac/Reusable/SystemMenu.swift rename to Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Providers.swift index 4281fd1b..f304c5e3 100644 --- a/Passepartout/Mac/Reusable/SystemMenu.swift +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct+Providers.swift @@ -1,8 +1,8 @@ // -// SystemMenu.swift +// AppProduct+Providers.swift // Passepartout // -// Created by Davide De Rosa on 7/2/22. +// Created by Davide De Rosa on 9/18/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,10 +23,8 @@ // along with Passepartout. If not, see . // -import AppKit import Foundation -@MainActor -protocol SystemMenu { - var asMenu: NSMenu { get } +extension AppProduct { + static let providerPrefix = "providers." } diff --git a/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct.swift new file mode 100644 index 00000000..b9b64d7a --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppProduct.swift @@ -0,0 +1,66 @@ +// +// AppProduct.swift +// Passepartout +// +// Created by Davide De Rosa on 10/11/19. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import Foundation +import PassepartoutKit +import UtilsLibrary + +public struct AppProduct: RawRepresentable, Hashable, Sendable { + public let rawValue: String + + public init?(rawValue: String) { + if let range = rawValue.range(of: Self.featurePrefix) ?? rawValue.range(of: Self.providerPrefix) { + self.rawValue = String(rawValue[range.lowerBound... // -import AppKit import Foundation +import UtilsLibrary -@MainActor -protocol ItemGroup { - func asMenuItems(withParent parent: NSMenu) -> [NSMenuItem] +public protocol AppReceiptReader: InAppReceiptReader where UserLevel == AppUserLevel { } diff --git a/PassepartoutLibrary/Sources/PassepartoutProviders/Extensions/ProviderName+Credentials.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppUserLevel+Features.swift similarity index 68% rename from PassepartoutLibrary/Sources/PassepartoutProviders/Extensions/ProviderName+Credentials.swift rename to Passepartout/Library/Sources/AppLibrary/IAP/AppUserLevel+Features.swift index d2ebcba0..56dae9f1 100644 --- a/PassepartoutLibrary/Sources/PassepartoutProviders/Extensions/ProviderName+Credentials.swift +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppUserLevel+Features.swift @@ -1,8 +1,8 @@ // -// ProviderName+Credentials.swift +// AppUserLevel+Features.swift // Passepartout // -// Created by Davide De Rosa on 3/25/22. +// Created by Davide De Rosa on 9/10/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,20 +24,20 @@ // import Foundation -import PassepartoutCore -extension ProviderName { - public func requiresCredentials(forProtocol vpnProtocol: VPNProtocolType) -> Bool { - vpnProtocol == .openVPN - } - - public var credentialsPurpose: CredentialsPurpose { +extension AppUserLevel: AppFeatureProviding { + var features: [AppFeature] { switch self { - case .protonvpn, .surfshark, .torguard, .windscribe: - return .specific + case .fullVersion: + return AppFeature.allCases + + case .fullVersionPlusTV: + var list = AppFeature.allCases + list.append(.appleTV) + return list default: - return .web + return [] } } } diff --git a/PassepartoutLibrary/Sources/PassepartoutFrontend/Domain/AppType.swift b/Passepartout/Library/Sources/AppLibrary/IAP/AppUserLevel.swift similarity index 81% rename from PassepartoutLibrary/Sources/PassepartoutFrontend/Domain/AppType.swift rename to Passepartout/Library/Sources/AppLibrary/IAP/AppUserLevel.swift index 1071ea29..ae56edf9 100644 --- a/PassepartoutLibrary/Sources/PassepartoutFrontend/Domain/AppType.swift +++ b/Passepartout/Library/Sources/AppLibrary/IAP/AppUserLevel.swift @@ -1,5 +1,5 @@ // -// AppType.swift +// AppUserLevel.swift // Passepartout // // Created by Davide De Rosa on 12/19/23. @@ -25,7 +25,7 @@ import Foundation -public enum AppType: Int { +public enum AppUserLevel: Int { case undefined = -1 case freemium = 0 @@ -36,7 +36,17 @@ public enum AppType: Int { case fullVersionPlusTV = 3 - public var isRestricted: Bool { + var isFullVersion: Bool { + switch self { + case .fullVersion, .fullVersionPlusTV: + return true + + default: + return false + } + } + + var isRestricted: Bool { switch self { case .undefined, .beta: return true diff --git a/Passepartout/Library/Sources/AppLibrary/IAP/IAPManager+ProfileProcessor.swift b/Passepartout/Library/Sources/AppLibrary/IAP/IAPManager+ProfileProcessor.swift new file mode 100644 index 00000000..7d49f625 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/IAP/IAPManager+ProfileProcessor.swift @@ -0,0 +1,43 @@ +// +// IAPManager+ProfileProcessor.swift +// Passepartout +// +// Created by Davide De Rosa on 9/10/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit + +extension IAPManager: ProfileProcessor { + func processedProfile(_ profile: Profile) throws -> Profile { + var builder = profile.builder() + + // suppress on-demand module if not eligible + if !isEligible(for: .onDemand) { + pp_log(.app, .notice, "Disable on-demand rules, not eligible") + builder.modules.removeAll { + $0 is OnDemandModule + } + } + + return try builder.tryBuild() + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/IAP/IAPManager.swift b/Passepartout/Library/Sources/AppLibrary/IAP/IAPManager.swift new file mode 100644 index 00000000..a47de11c --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/IAP/IAPManager.swift @@ -0,0 +1,171 @@ +// +// IAPManager.swift +// Passepartout +// +// Created by Davide De Rosa on 9/10/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit +import UtilsLibrary + +// TODO: #424, reload receipt + objectWillChange on purchase/transactions + +@MainActor +public final class IAPManager: ObservableObject { + private let customUserLevel: AppUserLevel? + + private let receiptReader: any AppReceiptReader + + private let productsAtBuild: BuildProducts? + + private(set) var userLevel: AppUserLevel + + private var purchasedAppBuild: Int? + + private(set) var purchasedProducts: Set + + private var eligibleFeatures: Set + + public init( + customUserLevel: AppUserLevel? = nil, + receiptReader: any AppReceiptReader, + productsAtBuild: BuildProducts? = nil + ) { + self.customUserLevel = customUserLevel + self.receiptReader = receiptReader + self.productsAtBuild = productsAtBuild + userLevel = .undefined + purchasedProducts = [] + eligibleFeatures = [] + } + + public func reloadReceipt() async { + await fetchLevelIfNeeded() + + if let receipt = await receiptReader.receipt(for: userLevel) { + if let originalBuildNumber = receipt.originalBuildNumber { + purchasedAppBuild = originalBuildNumber + } + purchasedProducts.removeAll() + + if let purchasedAppBuild { + pp_log(.app, .info, "Original purchased build: \(purchasedAppBuild)") + + // assume some purchases by build number + let entitled = productsAtBuild?(purchasedAppBuild) ?? [] + pp_log(.app, .notice, "Entitled features: \(entitled.map(\.rawValue))") + + entitled.forEach { + purchasedProducts.insert($0) + } + } + if let iapReceipts = receipt.purchaseReceipts { + pp_log(.app, .info, "In-app receipts:") + iapReceipts.forEach { + guard let pid = $0.productIdentifier, let product = AppProduct(rawValue: pid) else { + return + } + if let cancellationDate = $0.cancellationDate { + pp_log(.app, .info, "\t\(pid) [cancelled on: \(cancellationDate)]") + return + } + if let purchaseDate = $0.originalPurchaseDate { + pp_log(.app, .info, "\t\(pid) [purchased on: \(purchaseDate)]") + } + purchasedProducts.insert(product) + } + } + + eligibleFeatures = purchasedProducts.reduce(into: []) { eligible, product in + product.features.forEach { + eligible.insert($0) + } + } + } else { + pp_log(.app, .error, "Could not parse App Store receipt!") + + eligibleFeatures = Set(userLevel.features) + } + + pp_log(.app, .notice, "Purchased products: \(purchasedProducts.map(\.rawValue))") + pp_log(.app, .notice, "Eligible features: \(eligibleFeatures)") + objectWillChange.send() + } +} + +private extension IAPManager { + func fetchLevelIfNeeded() async { + guard userLevel == .undefined else { + return + } + if let customUserLevel { + userLevel = customUserLevel + pp_log(.app, .info, "App level (custom): \(userLevel)") + } else { + let isBeta = await SandboxChecker().isBeta + userLevel = isBeta ? .beta : .freemium + pp_log(.app, .info, "App level: \(userLevel)") + } + } +} + +// MARK: In-app eligibility + +extension IAPManager { + public var isRestricted: Bool { + userLevel.isRestricted + } + + public func isEligible(for feature: AppFeature) -> Bool { + eligibleFeatures.contains(feature) + } + + public func isEligible(for features: [AppFeature]) -> Bool { + features.allSatisfy(eligibleFeatures.contains) + } + +// public func isEligible(forProvider providerName: ProviderName) -> Bool { +// guard providerName != .oeck else { +// return true +// } +// return isEligible(forFeature: providerName.product) +// } + + public func isEligibleForFeedback() -> Bool { +#if os(tvOS) + false +#else + userLevel == .beta || isPayingUser() +#endif + } + + public func paywallReason(forFeature feature: AppFeature) -> PaywallReason? { + if isEligible(for: feature) { + return nil + } + return isRestricted ? .restricted : .purchase(feature) + } + + public func isPayingUser() -> Bool { + !purchasedProducts.isEmpty + } +} diff --git a/Passepartout/App/Domain/StoreKitReceiptReader.swift b/Passepartout/Library/Sources/AppLibrary/IAP/KvittoReceiptReader.swift similarity index 84% rename from Passepartout/App/Domain/StoreKitReceiptReader.swift rename to Passepartout/Library/Sources/AppLibrary/IAP/KvittoReceiptReader.swift index 0b4b08ee..2bc84923 100644 --- a/Passepartout/App/Domain/StoreKitReceiptReader.swift +++ b/Passepartout/Library/Sources/AppLibrary/IAP/KvittoReceiptReader.swift @@ -1,5 +1,5 @@ // -// StoreKitReceiptReader.swift +// KvittoReceiptReader.swift // Passepartout // // Created by Davide De Rosa on 12/20/23. @@ -25,12 +25,16 @@ import Foundation import Kvitto -import PassepartoutLibrary +import PassepartoutKit +import UtilsLibrary -struct StoreKitReceiptReader: ReceiptReader { - func receipt(for appType: AppType) -> InAppReceipt? { +public final class KvittoReceiptReader: AppReceiptReader { + public init() { + } + + public func receipt(for userLevel: AppUserLevel) -> InAppReceipt? { guard let url = Bundle.main.appStoreReceiptURL else { - pp_log.warning("No App Store receipt found!") + pp_log(.app, .error, "No App Store receipt found!") return nil } @@ -39,14 +43,16 @@ struct StoreKitReceiptReader: ReceiptReader { let fallbackReceipt: Receipt? = { // in TestFlight, attempt fallback to existing release receipt - if appType == .beta { + if userLevel == .beta { guard let receipt else { let releaseUrl = url.deletingLastPathComponent().appendingPathComponent("receipt") guard releaseUrl != url else { +#if !targetEnvironment(simulator) assertionFailure("How can release URL be equal to sandbox URL in TestFlight?") +#endif return nil } - pp_log.warning("Sandbox receipt not found, falling back to Release receipt") + pp_log(.app, .error, "Sandbox receipt not found, falling back to Release receipt") return Receipt(contentsOfURL: releaseUrl) } return receipt diff --git a/Passepartout/Library/Sources/AppLibrary/L10n/AppError+L10n.swift b/Passepartout/Library/Sources/AppLibrary/L10n/AppError+L10n.swift new file mode 100644 index 00000000..4acfa4f5 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/L10n/AppError+L10n.swift @@ -0,0 +1,119 @@ +// +// AppError+L10n.swift +// Passepartout +// +// Created by Davide De Rosa on 8/27/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import Foundation +import PassepartoutKit +import UtilsLibrary + +extension AppError: LocalizedError { + var errorDescription: String? { + let V = Strings.Errors.App.self + switch self { + case .emptyProfileName: + return V.emptyProfileName + + case .malformedModule(let module, let error): + return V.malformedModule(module.typeDescription, error.localizedDescription) + + case .ipModuleRequiresConnection: + return V.ipModuleRequiresConnection + + case .multipleConnectionModules: + return V.multipleConnectionModules + + case .permissionDenied: + return V.default + + case .generic(let error): + return error.localizedDescription + } + } +} + +// MARK: - App side + +extension PassepartoutError: LocalizedError { + public var errorDescription: String? { + switch code { + case .invalidFields: + guard let fields = userInfo as? [String: String?] else { + return nil + } + let fieldsDescription = fields + .map { + "\($0)=\($1?.description ?? "")" + } + .joined(separator: ",") + + return Strings.Errors.App.Passepartout.invalidFields(fieldsDescription) + + case .parsing: + return Strings.Errors.App.Passepartout.parsing + + case .unhandled: + return reason?.localizedDescription + + default: + return Strings.Errors.App.Passepartout.default(code.rawValue) + } + } +} + +// MARK: - Tunnel side + +extension PassepartoutError.Code: LocalizableEntity { + public var localizedDescription: String { + let V = Strings.Errors.Tunnel.self + switch self { + case .authentication: + return V.auth + + case .crypto: + return V.encryption + + case .dnsFailure: + return V.dns + + case .timeout: + return V.timeout + + case .OpenVPN.compressionMismatch: + return V.compression + + case .OpenVPN.noRouting: + return V.routing + + case .OpenVPN.serverShutdown: + return V.shutdown + + case .OpenVPN.tlsFailure: + return V.tls + + default: + return V.generic + } + } +} diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ObservableProfile.swift b/Passepartout/Library/Sources/AppLibrary/L10n/EditableModule+Description.swift similarity index 63% rename from PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ObservableProfile.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/EditableModule+Description.swift index 187ed27c..735c282e 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ObservableProfile.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/EditableModule+Description.swift @@ -1,8 +1,8 @@ // -// ObservableProfile.swift +// EditableModule+Description.swift // Passepartout // -// Created by Davide De Rosa on 6/20/22. +// Created by Davide De Rosa on 9/6/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,23 +24,21 @@ // import Foundation -import PassepartoutCore +import PassepartoutKit -public final class ObservableProfile: ValueHolder, ObservableObject { - @Published public internal(set) var isLoading = false +extension EditableModule { - @Published public var value: Profile - - public init() { - value = .placeholder + @MainActor + func description(inEditor editor: ProfileEditor) -> String { + editor.displayName(forModuleWithId: id) ?? typeDescription } } -extension ObservableProfile { - public var name: String { - guard !isLoading && !value.isPlaceholder else { - return "" +extension EditableModule { + var typeDescription: String { + guard let providing = self as? ModuleTypeProviding else { + return String(describing: self) } - return value.header.name + return providing.moduleType.localizedDescription } } diff --git a/Passepartout/App/Views/LogoView.swift b/Passepartout/Library/Sources/AppLibrary/L10n/ErrorHandler+Default.swift similarity index 63% rename from Passepartout/App/Views/LogoView.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/ErrorHandler+Default.swift index 490e1dfe..65461c7e 100644 --- a/Passepartout/App/Views/LogoView.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/ErrorHandler+Default.swift @@ -1,8 +1,8 @@ // -// LogoView.swift +// ErrorHandler+Default.swift // Passepartout // -// Created by Davide De Rosa on 3/20/23. +// Created by Davide De Rosa on 8/22/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,13 +23,21 @@ // along with Passepartout. If not, see . // +import PassepartoutKit import SwiftUI +import UtilsLibrary -struct LogoView: View { - var body: some View { - ZStack { - themePrimaryBackground - Image(themeAssetsLogoImage) - }.ignoresSafeArea() +extension ErrorHandler { + static func `default`() -> ErrorHandler { + ErrorHandler( + defaultTitle: Strings.Unlocalized.appName, + dismissTitle: Strings.Global.ok, + errorDescription: { + AppError($0).localizedDescription + }, + beforeAlert: { + pp_log(.app, .error, $0) + } + ) } } diff --git a/Passepartout/Mac/Mac/DefaultMacMenu.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Foundation+L10n.swift similarity index 65% rename from Passepartout/Mac/Mac/DefaultMacMenu.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/Foundation+L10n.swift index 9e6a3011..07ee65d0 100644 --- a/Passepartout/Mac/Mac/DefaultMacMenu.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Foundation+L10n.swift @@ -1,8 +1,8 @@ // -// DefaultMacMenu.swift +// Foundation+L10n.swift // Passepartout // -// Created by Davide De Rosa on 6/19/22. +// Created by Davide De Rosa on 8/18/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,18 +24,21 @@ // import Foundation +import UtilsLibrary -final class DefaultMacMenu: MacMenu { - weak var delegate: MacMenuDelegate? +extension TimeInterval: StyledLocalizableEntity { + public enum Style { + case timeString + } - private lazy var menu: PassepartoutMenu = { - guard let delegate = delegate else { - fatalError("Must set MacMenu.delegate") + public func localizedDescription(style: Style) -> String { + switch style { + case .timeString: + if self > 0 { + return asTimeString + } else { + return Strings.Global.disabled + } } - return PassepartoutMenu(macMenuDelegate: delegate) - }() - - func install() { - menu.install() } } diff --git a/Passepartout/Library/Sources/AppLibrary/L10n/ModuleType+L10n.swift b/Passepartout/Library/Sources/AppLibrary/L10n/ModuleType+L10n.swift new file mode 100644 index 00000000..aa6d628b --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/L10n/ModuleType+L10n.swift @@ -0,0 +1,51 @@ +// +// ModuleType+L10n.swift +// Passepartout +// +// Created by Davide De Rosa on 7/1/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import UtilsLibrary + +extension ModuleType: LocalizableEntity { + public var localizedDescription: String { + switch self { + case .openVPN: + return Strings.Unlocalized.openVPN + + case .wireGuard: + return Strings.Unlocalized.wireGuard + + case .dns: + return Strings.Unlocalized.dns + + case .httpProxy: + return Strings.Unlocalized.httpProxy + + case .ip: + return Strings.Unlocalized.ip + + case .onDemand: + return Strings.Global.onDemand + } + } +} diff --git a/Passepartout/App/Extensions/VPNProtocolType+FileExtensions.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/DNSModule+L10n.swift similarity index 67% rename from Passepartout/App/Extensions/VPNProtocolType+FileExtensions.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/Modules/DNSModule+L10n.swift index ea85aee2..975ecb9a 100644 --- a/Passepartout/App/Extensions/VPNProtocolType+FileExtensions.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/DNSModule+L10n.swift @@ -1,8 +1,8 @@ // -// VPNProtocolType+FileExtensions.swift +// DNSModule+L10n.swift // Passepartout // -// Created by Davide De Rosa on 4/7/22. +// Created by Davide De Rosa on 2/24/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,21 +24,20 @@ // import Foundation -import PassepartoutLibrary +import PassepartoutKit +import UtilsLibrary -extension VPNProtocolType { - static let knownFileExtensions: [String] = { - let protos: [Self] = [.openVPN, .wireGuard] - return protos.map(\.fileExtension) - }() - - var fileExtension: String { +extension DNSProtocol: LocalizableEntity { + public var localizedDescription: String { switch self { - case .openVPN: - return "ovpn" + case .cleartext: + return Strings.Entities.DnsProtocol.cleartext - case .wireGuard: - return "conf" + case .https: + return Strings.Entities.DnsProtocol.https + + case .tls: + return Strings.Entities.DnsProtocol.tls } } } diff --git a/Passepartout/Tunnel/TunnelError.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/HTTPProxyModule+L10n.swift similarity index 87% rename from Passepartout/Tunnel/TunnelError.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/Modules/HTTPProxyModule+L10n.swift index 1f045cff..24cbd022 100644 --- a/Passepartout/Tunnel/TunnelError.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/HTTPProxyModule+L10n.swift @@ -1,8 +1,8 @@ // -// TunnelError.swift +// HTTPProxyModule+L10n.swift // Passepartout // -// Created by Davide De Rosa on 12/23/23. +// Created by Davide De Rosa on 2/24/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,7 +24,5 @@ // import Foundation - -enum TunnelError: Error { - case expired -} +import PassepartoutKit +import UtilsLibrary diff --git a/Passepartout/App/Views/AddHostView.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/IPModule+L10n.swift similarity index 89% rename from Passepartout/App/Views/AddHostView.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/Modules/IPModule+L10n.swift index a4891b35..87eda418 100644 --- a/Passepartout/App/Views/AddHostView.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/IPModule+L10n.swift @@ -1,8 +1,8 @@ // -// AddHostView.swift +// IPModule+L10n.swift // Passepartout // -// Created by Davide De Rosa on 4/26/22. +// Created by Davide De Rosa on 8/20/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,7 +23,4 @@ // along with Passepartout. If not, see . // -import SwiftUI - -enum AddHostView { -} +import Foundation diff --git a/PassepartoutLibrary/Sources/PassepartoutProviders/Domain/ProviderName.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/OnDemandModule+L10n.swift similarity index 88% rename from PassepartoutLibrary/Sources/PassepartoutProviders/Domain/ProviderName.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/Modules/OnDemandModule+L10n.swift index df2313c2..e4334ac0 100644 --- a/PassepartoutLibrary/Sources/PassepartoutProviders/Domain/ProviderName.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/OnDemandModule+L10n.swift @@ -1,8 +1,8 @@ // -// ProviderName.swift +// OnDemandModule+L10n.swift // Passepartout // -// Created by Davide De Rosa on 3/15/22. +// Created by Davide De Rosa on 8/20/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,5 +24,3 @@ // import Foundation - -public typealias ProviderName = String diff --git a/Passepartout/Library/Sources/AppLibrary/L10n/Modules/OpenVPNModule+L10n.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/OpenVPNModule+L10n.swift new file mode 100644 index 00000000..b47136cc --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/OpenVPNModule+L10n.swift @@ -0,0 +1,221 @@ +// +// OpenVPNModule+L10n.swift +// Passepartout +// +// Created by Davide De Rosa on 8/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit +import UtilsLibrary + +extension OpenVPN.PullMask: LocalizableEntity { + public var localizedDescription: String { + switch self { + case .routes: + return Strings.Global.routes + + case .dns: + return Strings.Unlocalized.dns + + case .proxy: + return Strings.Unlocalized.proxy + } + } +} + +extension OpenVPN.Cipher: LocalizableEntity { + public var localizedDescription: String { + description + } +} + +extension OpenVPN.Digest: LocalizableEntity { + public var localizedDescription: String { + description + } +} + +extension OpenVPN.CompressionFraming: LocalizableEntity { + public var localizedDescription: String { + switch self { + case .disabled: + return Strings.Global.disabled + + case .compLZO: + return Strings.Unlocalized.OpenVPN.compLZO + + case .compress, .compressV2: + return Strings.Unlocalized.OpenVPN.compress + + default: + return Strings.Global.unknown + } + } +} + +extension OpenVPN.CompressionAlgorithm: LocalizableEntity { + public var localizedDescription: String { + switch self { + case .disabled: + return Strings.Global.disabled + + case .LZO: + return Strings.Unlocalized.OpenVPN.lzo + + case .other: + return Strings.Entities.Openvpn.CompressionAlgorithm.other + + default: + return Strings.Global.unknown + } + } +} + +extension OpenVPN.XORMethod: StyledLocalizableEntity { + public enum Style { + case short + + case long + } + + public func localizedDescription(style: Style) -> String { + switch style { + case .short: + return shortDescription + + case .long: + return longDescription + } + } + + private var shortDescription: String { + let V = Strings.Unlocalized.OpenVPN.XOR.self + switch self { + case .xormask: + return V.xormask.rawValue + + case .xorptrpos: + return V.xorptrpos.rawValue + + case .reverse: + return V.reverse.rawValue + + case .obfuscate: + return V.obfuscate.rawValue + } + } + + private var longDescription: String { + switch self { + case .xormask(let mask): + return "\(shortDescription) \(mask.zData.toHex())" + + case .obfuscate(let mask): + return "\(shortDescription) \(mask.zData.toHex())" + + default: + return shortDescription + } + } +} + +extension OpenVPN.Configuration.Builder: StyledLocalizableEntity { + public enum Style { + case tlsWrap + + case eku + } + + public func localizedDescription(style: Style) -> String { + switch style { + case .tlsWrap: + return tlsWrap.tlsWrapDescription + + case .eku: + return checksEKU.ekuDescription + } + } +} + +extension OpenVPN.Configuration.Builder: StyledOptionalLocalizableEntity { + public enum OptionalStyle { + case keepAlive + + case renegotiatesAfter + + case randomizeEndpoint + + case randomizeHostnames + } + + public func localizedDescription(optionalStyle: OptionalStyle) -> String? { + switch optionalStyle { + case .keepAlive: + return keepAliveInterval?.localizedDescription(style: .timeString) + + case .renegotiatesAfter: + return renegotiatesAfter?.localizedDescription(style: .timeString) + + case .randomizeEndpoint: + return randomizeEndpoint?.randomizeEndpointDescription + + case .randomizeHostnames: + return randomizeHostnames?.randomizeHostnamesDescription + } + } +} + +// MARK: - Raw types + +private extension Optional where Wrapped == OpenVPN.TLSWrap { + var tlsWrapDescription: String { + guard let strategy = self?.strategy else { + return Strings.Global.disabled + } + switch strategy { + case .auth: + return "--tls-auth" + + case .crypt: + return "--tls-crypt" + } + } +} + +private extension Optional where Wrapped == Bool { + var ekuDescription: String { + let V = Strings.Global.self + return (self ?? false) ? V.enabled : V.disabled + } +} + +private extension Bool { + var randomizeEndpointDescription: String { + let V = Strings.Global.self + return self ? V.enabled : V.disabled + } + + var randomizeHostnamesDescription: String { + let V = Strings.Global.self + return self ? V.enabled : V.disabled + } +} diff --git a/PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSProviderName.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/WireGuardModule+L10n.swift similarity index 88% rename from PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSProviderName.swift rename to Passepartout/Library/Sources/AppLibrary/L10n/Modules/WireGuardModule+L10n.swift index 0cbc4ce7..f092195f 100644 --- a/PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSProviderName.swift +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Modules/WireGuardModule+L10n.swift @@ -1,8 +1,8 @@ // -// WSProviderName.swift +// WireGuardModule+L10n.swift // Passepartout // -// Created by Davide De Rosa on 11/24/19. +// Created by Davide De Rosa on 8/18/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,5 +24,3 @@ // import Foundation - -public typealias WSProviderName = String diff --git a/Passepartout/Library/Sources/AppLibrary/L10n/PassepartoutKit+L10n.swift b/Passepartout/Library/Sources/AppLibrary/L10n/PassepartoutKit+L10n.swift new file mode 100644 index 00000000..62a85189 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/L10n/PassepartoutKit+L10n.swift @@ -0,0 +1,183 @@ +// +// PassepartoutKit+L10n.swift +// Passepartout +// +// Created by Davide De Rosa on 2/19/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit +import UtilsLibrary + +extension TunnelStatus: LocalizableEntity { + public var localizedDescription: String { + let V = Strings.Entities.TunnelStatus.self + switch self { + case .inactive: + return V.inactive + + case .activating: + return V.activating + + case .active: + return V.active + + case .deactivating: + return V.deactivating + } + } +} + +extension ConnectionStatus: LocalizableEntity { + public var localizedDescription: String { + let V = Strings.Entities.ConnectionStatus.self + switch self { + case .disconnected: + return V.disconnected + + case .connecting: + return V.connecting + + case .connected: + return V.connected + + case .disconnecting: + return V.disconnecting + } + } +} + +extension DataCount: LocalizableEntity { + public var localizedDescription: String { + let down = received.descriptionAsDataUnit + let up = sent.descriptionAsDataUnit + return "↓\(down) ↑\(up)" + } +} + +extension Address.Family: LocalizableEntity { + public var localizedDescription: String { + switch self { + case .v4: + return Strings.Unlocalized.ipv4 + + case .v6: + return Strings.Unlocalized.ipv6 + } + } +} + +extension IPSettings: StyledOptionalLocalizableEntity { + public enum OptionalStyle { + case address + + case defaultGateway + } + + public func localizedDescription(optionalStyle: OptionalStyle) -> String? { + switch optionalStyle { + case .address: + return addressDescription + + case .defaultGateway: + return defaultGatewayDescription + } + } + + private var addressDescription: String? { + subnet?.address.rawValue + } + + private var defaultGatewayDescription: String? { + includedRoutes + .first(where: \.isDefault)? + .gateway? + .rawValue + } +} + +extension Route: LocalizableEntity { + public var localizedDescription: String { + if let dest = destination?.rawValue { + if let gw = gateway?.rawValue { + return "\(dest) → \(gw)" + } else { + return dest + } + } else if let gw = gateway?.rawValue { + return "default → \(gw)" + } + return "default → *" + } +} + +extension OnDemandModule.Policy: LocalizableEntity { + public var localizedDescription: String { + switch self { + case .any: + return Strings.Entities.OnDemand.Policy.any + + case .excluding: + return Strings.Entities.OnDemand.Policy.excluding + + case .including: + return Strings.Entities.OnDemand.Policy.including + } + } +} + +extension OpenVPN.Credentials.OTPMethod: StyledLocalizableEntity { + public enum Style { + case entity + + case approachDescription + } + + public func localizedDescription(style: Style) -> String { + switch style { + case .entity: + let V = Strings.Entities.Openvpn.OtpMethod.self + switch self { + case .none: + return V.none + + case .append: + return V.append + + case .encode: + return V.encode + } + + case .approachDescription: + let V = Strings.Modules.Openvpn.Credentials.OtpMethod.Approach.self + switch self { + case .none: + return "" + + case .append: + return V.append + + case .encode: + return V.encode + } + } + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/L10n/Strings+Unlocalized.swift b/Passepartout/Library/Sources/AppLibrary/L10n/Strings+Unlocalized.swift new file mode 100644 index 00000000..fabdc876 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/L10n/Strings+Unlocalized.swift @@ -0,0 +1,139 @@ +// +// Strings+Unlocalized.swift +// Passepartout +// +// Created by Davide De Rosa on 7/2/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit + +extension Strings { + enum Unlocalized { + enum OpenVPN { + enum XOR: String { + case xormask + + case xorptrpos + + case reverse + + case obfuscate + } + + static let compLZO = "--comp-lzo" + + static let compress = "--compress" + + static let lzo = "LZO" + } + + enum Placeholders { + static let hostname = "example.com" + + static let dohURL = "https://1.2.3.4/some-query" + + static let dotHostname = "dns-hostname.com" + + static let ipV4DNS = "1.1.1.1" + + static func ipDestination(forFamily family: Address.Family) -> String { + switch family { + case .v4: + return "192.168.15.0/24" + + case .v6: + return "fdbd:dcf8:d811:af73::/64" + } + } + + static func ipGateway(forFamily family: Address.Family) -> String { + switch family { + case .v4: + return "192.168.15.1" + + case .v6: + return "fdbd:dcf8:d811:af73::1" + } + } + + static let mtu = "1500" + + static let proxyIPv4Address = "192.168.1.1" + + static let proxyPort = "1080" + + static let pacURL = "http://proxy.com/pac.url" + } + + enum Issues { + static let subject = "\(appName) - Report issue" + + static let attachmentMimeType = "text/plain" + + static let appLogFilename = "app.log" + + static let tunnelLogFilename = "tunnel.log" + } + + static let appName = "Passepartout" + + static let ca = "CA" + + static let dns = "DNS" + + static let faq = "FAQ" + + static let http = "HTTP" + + static let https = "HTTPS" + + static let httpProxy = "HTTP Proxy" + + static let ip = "IP" + + static let ipv4 = "IPv4" + + static let ipv6 = "IPv6" + + static let mtu = "MTU" + + static let openVPN = "OpenVPN" + + static let otp = "OTP" + + static let pac = "PAC" + + static let proxy = "Proxy" + + static let tls = "TLS" + + static let url = "URL" + + static let uuid = "UUID" + + static let wifi = "Wi-Fi" + + static let wireGuard = "WireGuard" + + static let xor = "XOR" + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/L10n/SwiftGen+Strings.swift b/Passepartout/Library/Sources/AppLibrary/L10n/SwiftGen+Strings.swift new file mode 100644 index 00000000..bf6d12b9 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/L10n/SwiftGen+Strings.swift @@ -0,0 +1,550 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +import Foundation + +// swiftlint:disable superfluous_disable_command file_length implicit_return prefer_self_in_static_references + +// MARK: - Strings + +// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length +// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces +internal enum Strings { + internal enum Alerts { + internal enum Iap { + internal enum Restricted { + /// The requested feature is unavailable in this build. + internal static let message = Strings.tr("Localizable", "alerts.iap.restricted.message", fallback: "The requested feature is unavailable in this build.") + /// Restricted + internal static let title = Strings.tr("Localizable", "alerts.iap.restricted.title", fallback: "Restricted") + } + } + } + internal enum Entities { + internal enum ConnectionStatus { + /// Connected + internal static let connected = Strings.tr("Localizable", "entities.connection_status.connected", fallback: "Connected") + /// Connecting + internal static let connecting = Strings.tr("Localizable", "entities.connection_status.connecting", fallback: "Connecting") + /// Disconnected + internal static let disconnected = Strings.tr("Localizable", "entities.connection_status.disconnected", fallback: "Disconnected") + /// Disconnecting + internal static let disconnecting = Strings.tr("Localizable", "entities.connection_status.disconnecting", fallback: "Disconnecting") + } + internal enum Dns { + /// Search domains + internal static let searchDomains = Strings.tr("Localizable", "entities.dns.search_domains", fallback: "Search domains") + /// Servers + internal static let servers = Strings.tr("Localizable", "entities.dns.servers", fallback: "Servers") + } + internal enum DnsProtocol { + /// Cleartext + internal static let cleartext = Strings.tr("Localizable", "entities.dns_protocol.cleartext", fallback: "Cleartext") + /// Over HTTPS + internal static let https = Strings.tr("Localizable", "entities.dns_protocol.https", fallback: "Over HTTPS") + /// Over TLS + internal static let tls = Strings.tr("Localizable", "entities.dns_protocol.tls", fallback: "Over TLS") + } + internal enum HttpProxy { + /// Bypass domains + internal static let bypassDomains = Strings.tr("Localizable", "entities.http_proxy.bypass_domains", fallback: "Bypass domains") + } + internal enum OnDemand { + internal enum Policy { + /// All networks + internal static let any = Strings.tr("Localizable", "entities.on_demand.policy.any", fallback: "All networks") + /// Excluding + internal static let excluding = Strings.tr("Localizable", "entities.on_demand.policy.excluding", fallback: "Excluding") + /// Including + internal static let including = Strings.tr("Localizable", "entities.on_demand.policy.including", fallback: "Including") + } + } + internal enum Openvpn { + internal enum CompressionAlgorithm { + /// Unsupported + internal static let other = Strings.tr("Localizable", "entities.openvpn.compression_algorithm.other", fallback: "Unsupported") + } + internal enum OtpMethod { + /// Append + internal static let append = Strings.tr("Localizable", "entities.openvpn.otp_method.append", fallback: "Append") + /// Encode + internal static let encode = Strings.tr("Localizable", "entities.openvpn.otp_method.encode", fallback: "Encode") + /// None + internal static let `none` = Strings.tr("Localizable", "entities.openvpn.otp_method.none", fallback: "None") + } + } + internal enum Profile { + internal enum Name { + /// New profile + internal static let new = Strings.tr("Localizable", "entities.profile.name.new", fallback: "New profile") + } + } + internal enum TunnelStatus { + /// Activating + internal static let activating = Strings.tr("Localizable", "entities.tunnel_status.activating", fallback: "Activating") + /// Active + internal static let active = Strings.tr("Localizable", "entities.tunnel_status.active", fallback: "Active") + /// Deactivating + internal static let deactivating = Strings.tr("Localizable", "entities.tunnel_status.deactivating", fallback: "Deactivating") + /// Inactive + internal static let inactive = Strings.tr("Localizable", "entities.tunnel_status.inactive", fallback: "Inactive") + } + } + internal enum Errors { + internal enum App { + /// Unable to complete operation. + internal static let `default` = Strings.tr("Localizable", "errors.app.default", fallback: "Unable to complete operation.") + /// Profile name is empty. + internal static let emptyProfileName = Strings.tr("Localizable", "errors.app.empty_profile_name", fallback: "Profile name is empty.") + /// IP module can only be enabled together with a connection. + internal static let ipModuleRequiresConnection = Strings.tr("Localizable", "errors.app.ip_module_requires_connection", fallback: "IP module can only be enabled together with a connection.") + /// Module %@ is malformed. %@ + internal static func malformedModule(_ p1: Any, _ p2: Any) -> String { + return Strings.tr("Localizable", "errors.app.malformed_module", String(describing: p1), String(describing: p2), fallback: "Module %@ is malformed. %@") + } + /// Only one connection module can be active at a time. + internal static let multipleConnectionModules = Strings.tr("Localizable", "errors.app.multiple_connection_modules", fallback: "Only one connection module can be active at a time.") + internal enum Passepartout { + /// Unable to complete operation (code=%@). + internal static func `default`(_ p1: Any) -> String { + return Strings.tr("Localizable", "errors.app.passepartout.default", String(describing: p1), fallback: "Unable to complete operation (code=%@).") + } + /// Invalid fields (%@). + internal static func invalidFields(_ p1: Any) -> String { + return Strings.tr("Localizable", "errors.app.passepartout.invalid_fields", String(describing: p1), fallback: "Invalid fields (%@).") + } + /// Unable to parse file. + internal static let parsing = Strings.tr("Localizable", "errors.app.passepartout.parsing", fallback: "Unable to parse file.") + } + } + internal enum Tunnel { + /// Auth failed + internal static let auth = Strings.tr("Localizable", "errors.tunnel.auth", fallback: "Auth failed") + /// Compression unsupported + internal static let compression = Strings.tr("Localizable", "errors.tunnel.compression", fallback: "Compression unsupported") + /// DNS failed + internal static let dns = Strings.tr("Localizable", "errors.tunnel.dns", fallback: "DNS failed") + /// Encryption failed + internal static let encryption = Strings.tr("Localizable", "errors.tunnel.encryption", fallback: "Encryption failed") + /// Failed + internal static let generic = Strings.tr("Localizable", "errors.tunnel.generic", fallback: "Failed") + /// Missing routing + internal static let routing = Strings.tr("Localizable", "errors.tunnel.routing", fallback: "Missing routing") + /// Server shutdown + internal static let shutdown = Strings.tr("Localizable", "errors.tunnel.shutdown", fallback: "Server shutdown") + /// Timeout + internal static let timeout = Strings.tr("Localizable", "errors.tunnel.timeout", fallback: "Timeout") + /// TLS failed + internal static let tls = Strings.tr("Localizable", "errors.tunnel.tls", fallback: "TLS failed") + } + } + internal enum Global { + /// Account + internal static let account = Strings.tr("Localizable", "global.account", fallback: "Account") + /// Address + internal static let address = Strings.tr("Localizable", "global.address", fallback: "Address") + /// Addresses + internal static let addresses = Strings.tr("Localizable", "global.addresses", fallback: "Addresses") + /// Any + internal static let any = Strings.tr("Localizable", "global.any", fallback: "Any") + /// Cancel + internal static let cancel = Strings.tr("Localizable", "global.cancel", fallback: "Cancel") + /// Certificate + internal static let certificate = Strings.tr("Localizable", "global.certificate", fallback: "Certificate") + /// Compression + internal static let compression = Strings.tr("Localizable", "global.compression", fallback: "Compression") + /// Connect + internal static let connect = Strings.tr("Localizable", "global.connect", fallback: "Connect") + /// Connection + internal static let connection = Strings.tr("Localizable", "global.connection", fallback: "Connection") + /// Default + internal static let `default` = Strings.tr("Localizable", "global.default", fallback: "Default") + /// Destination + internal static let destination = Strings.tr("Localizable", "global.destination", fallback: "Destination") + /// Disable + internal static let disable = Strings.tr("Localizable", "global.disable", fallback: "Disable") + /// Disabled + internal static let disabled = Strings.tr("Localizable", "global.disabled", fallback: "Disabled") + /// Disconnect + internal static let disconnect = Strings.tr("Localizable", "global.disconnect", fallback: "Disconnect") + /// Domain + internal static let domain = Strings.tr("Localizable", "global.domain", fallback: "Domain") + /// Done + internal static let done = Strings.tr("Localizable", "global.done", fallback: "Done") + /// Duplicate + internal static let duplicate = Strings.tr("Localizable", "global.duplicate", fallback: "Duplicate") + /// Edit + internal static let edit = Strings.tr("Localizable", "global.edit", fallback: "Edit") + /// Empty + internal static let empty = Strings.tr("Localizable", "global.empty", fallback: "Empty") + /// Enable + internal static let enable = Strings.tr("Localizable", "global.enable", fallback: "Enable") + /// Enabled + internal static let enabled = Strings.tr("Localizable", "global.enabled", fallback: "Enabled") + /// Endpoint + internal static let endpoint = Strings.tr("Localizable", "global.endpoint", fallback: "Endpoint") + /// Folder + internal static let folder = Strings.tr("Localizable", "global.folder", fallback: "Folder") + /// Gateway + internal static let gateway = Strings.tr("Localizable", "global.gateway", fallback: "Gateway") + /// General + internal static let general = Strings.tr("Localizable", "global.general", fallback: "General") + /// Hostname + internal static let hostname = Strings.tr("Localizable", "global.hostname", fallback: "Hostname") + /// Interface + internal static let interface = Strings.tr("Localizable", "global.interface", fallback: "Interface") + /// Keep-alive + internal static let keepAlive = Strings.tr("Localizable", "global.keep_alive", fallback: "Keep-alive") + /// Key + internal static let key = Strings.tr("Localizable", "global.key", fallback: "Key") + /// Method + internal static let method = Strings.tr("Localizable", "global.method", fallback: "Method") + /// Modules + internal static let modules = Strings.tr("Localizable", "global.modules", fallback: "Modules") + /// %d seconds + internal static func nSeconds(_ p1: Int) -> String { + return Strings.tr("Localizable", "global.n_seconds", p1, fallback: "%d seconds") + } + /// Name + internal static let name = Strings.tr("Localizable", "global.name", fallback: "Name") + /// Networks + internal static let networks = Strings.tr("Localizable", "global.networks", fallback: "Networks") + /// No content + internal static let noContent = Strings.tr("Localizable", "global.no_content", fallback: "No content") + /// No selection + internal static let noSelection = Strings.tr("Localizable", "global.no_selection", fallback: "No selection") + /// None + internal static let `none` = Strings.tr("Localizable", "global.none", fallback: "None") + /// OK + internal static let ok = Strings.tr("Localizable", "global.ok", fallback: "OK") + /// On demand + internal static let onDemand = Strings.tr("Localizable", "global.on_demand", fallback: "On demand") + /// Other + internal static let other = Strings.tr("Localizable", "global.other", fallback: "Other") + /// Password + internal static let password = Strings.tr("Localizable", "global.password", fallback: "Password") + /// Port + internal static let port = Strings.tr("Localizable", "global.port", fallback: "Port") + /// Private key + internal static let privateKey = Strings.tr("Localizable", "global.private_key", fallback: "Private key") + /// Protocol + internal static let `protocol` = Strings.tr("Localizable", "global.protocol", fallback: "Protocol") + /// Public key + internal static let publicKey = Strings.tr("Localizable", "global.public_key", fallback: "Public key") + /// Delete + internal static let remove = Strings.tr("Localizable", "global.remove", fallback: "Delete") + /// Restart + internal static let restart = Strings.tr("Localizable", "global.restart", fallback: "Restart") + /// Route + internal static let route = Strings.tr("Localizable", "global.route", fallback: "Route") + /// Routes + internal static let routes = Strings.tr("Localizable", "global.routes", fallback: "Routes") + /// Save + internal static let save = Strings.tr("Localizable", "global.save", fallback: "Save") + /// Server + internal static let server = Strings.tr("Localizable", "global.server", fallback: "Server") + /// Servers + internal static let servers = Strings.tr("Localizable", "global.servers", fallback: "Servers") + /// Status + internal static let status = Strings.tr("Localizable", "global.status", fallback: "Status") + /// Storage + internal static let storage = Strings.tr("Localizable", "global.storage", fallback: "Storage") + /// Subnet + internal static let subnet = Strings.tr("Localizable", "global.subnet", fallback: "Subnet") + /// Uninstall + internal static let uninstall = Strings.tr("Localizable", "global.uninstall", fallback: "Uninstall") + /// Unknown + internal static let unknown = Strings.tr("Localizable", "global.unknown", fallback: "Unknown") + /// Username + internal static let username = Strings.tr("Localizable", "global.username", fallback: "Username") + /// Version + internal static let version = Strings.tr("Localizable", "global.version", fallback: "Version") + } + internal enum Modules { + internal enum Dns { + internal enum SearchDomains { + /// Add domain + internal static let add = Strings.tr("Localizable", "modules.dns.search_domains.add", fallback: "Add domain") + } + internal enum Servers { + /// Add address + internal static let add = Strings.tr("Localizable", "modules.dns.servers.add", fallback: "Add address") + } + } + internal enum HttpProxy { + internal enum BypassDomains { + /// Add bypass domain + internal static let add = Strings.tr("Localizable", "modules.http_proxy.bypass_domains.add", fallback: "Add bypass domain") + } + } + internal enum Ip { + internal enum Routes { + /// Add %@ + internal static func addFamily(_ p1: Any) -> String { + return Strings.tr("Localizable", "modules.ip.routes.add_family", String(describing: p1), fallback: "Add %@") + } + /// Exclude route + internal static let exclude = Strings.tr("Localizable", "modules.ip.routes.exclude", fallback: "Exclude route") + /// Excluded routes + internal static let excluded = Strings.tr("Localizable", "modules.ip.routes.excluded", fallback: "Excluded routes") + /// Include route + internal static let include = Strings.tr("Localizable", "modules.ip.routes.include", fallback: "Include route") + /// Included routes + internal static let included = Strings.tr("Localizable", "modules.ip.routes.included", fallback: "Included routes") + } + } + internal enum OnDemand { + /// Ethernet + internal static let ethernet = Strings.tr("Localizable", "modules.on_demand.ethernet", fallback: "Ethernet") + /// Mobile + internal static let mobile = Strings.tr("Localizable", "modules.on_demand.mobile", fallback: "Mobile") + /// Policy + internal static let policy = Strings.tr("Localizable", "modules.on_demand.policy", fallback: "Policy") + internal enum Policy { + /// Activate the VPN %@. + internal static func footer(_ p1: Any) -> String { + return Strings.tr("Localizable", "modules.on_demand.policy.footer", String(describing: p1), fallback: "Activate the VPN %@.") + } + internal enum Footer { + /// in any network + internal static let any = Strings.tr("Localizable", "modules.on_demand.policy.footer.any", fallback: "in any network") + /// except in the networks below + internal static let excluding = Strings.tr("Localizable", "modules.on_demand.policy.footer.excluding", fallback: "except in the networks below") + /// only in the networks below + internal static let including = Strings.tr("Localizable", "modules.on_demand.policy.footer.including", fallback: "only in the networks below") + } + } + internal enum Ssid { + /// Add SSID + internal static let add = Strings.tr("Localizable", "modules.on_demand.ssid.add", fallback: "Add SSID") + } + } + internal enum Openvpn { + /// Cipher + internal static let cipher = Strings.tr("Localizable", "modules.openvpn.cipher", fallback: "Cipher") + /// Communication + internal static let communication = Strings.tr("Localizable", "modules.openvpn.communication", fallback: "Communication") + /// Compression + internal static let compression = Strings.tr("Localizable", "modules.openvpn.compression", fallback: "Compression") + /// Algorithm + internal static let compressionAlgorithm = Strings.tr("Localizable", "modules.openvpn.compression_algorithm", fallback: "Algorithm") + /// Framing + internal static let compressionFraming = Strings.tr("Localizable", "modules.openvpn.compression_framing", fallback: "Framing") + /// Credentials + internal static let credentials = Strings.tr("Localizable", "modules.openvpn.credentials", fallback: "Credentials") + /// Digest + internal static let digest = Strings.tr("Localizable", "modules.openvpn.digest", fallback: "Digest") + /// Extended verification + internal static let eku = Strings.tr("Localizable", "modules.openvpn.eku", fallback: "Extended verification") + /// Pull + internal static let pull = Strings.tr("Localizable", "modules.openvpn.pull", fallback: "Pull") + /// Randomize endpoint + internal static let randomizeEndpoint = Strings.tr("Localizable", "modules.openvpn.randomize_endpoint", fallback: "Randomize endpoint") + /// Randomize hostname + internal static let randomizeHostname = Strings.tr("Localizable", "modules.openvpn.randomize_hostname", fallback: "Randomize hostname") + /// Redirect gateway + internal static let redirectGateway = Strings.tr("Localizable", "modules.openvpn.redirect_gateway", fallback: "Redirect gateway") + /// Remotes + internal static let remotes = Strings.tr("Localizable", "modules.openvpn.remotes", fallback: "Remotes") + /// Renegotiation + internal static let renegotiation = Strings.tr("Localizable", "modules.openvpn.renegotiation", fallback: "Renegotiation") + /// Wrapping + internal static let tlsWrap = Strings.tr("Localizable", "modules.openvpn.tls_wrap", fallback: "Wrapping") + internal enum Credentials { + /// Interactive + internal static let interactive = Strings.tr("Localizable", "modules.openvpn.credentials.interactive", fallback: "Interactive") + internal enum Interactive { + /// On-demand will be disabled. + internal static let footer = Strings.tr("Localizable", "modules.openvpn.credentials.interactive.footer", fallback: "On-demand will be disabled.") + } + internal enum OtpMethod { + internal enum Approach { + /// The OTP will be appended to the password. + internal static let append = Strings.tr("Localizable", "modules.openvpn.credentials.otp_method.approach.append", fallback: "The OTP will be appended to the password.") + /// The OTP will be encoded in Base64 with the password. + internal static let encode = Strings.tr("Localizable", "modules.openvpn.credentials.otp_method.approach.encode", fallback: "The OTP will be encoded in Base64 with the password.") + } + } + } + } + internal enum Wireguard { + /// Allowed IPs + internal static let allowedIps = Strings.tr("Localizable", "modules.wireguard.allowed_ips", fallback: "Allowed IPs") + /// Interface + internal static let interface = Strings.tr("Localizable", "modules.wireguard.interface", fallback: "Interface") + /// Peer #%d + internal static func peer(_ p1: Int) -> String { + return Strings.tr("Localizable", "modules.wireguard.peer", p1, fallback: "Peer #%d") + } + /// Pre-shared key + internal static let presharedKey = Strings.tr("Localizable", "modules.wireguard.preshared_key", fallback: "Pre-shared key") + } + } + internal enum Placeholders { + /// secret + internal static let secret = Strings.tr("Localizable", "placeholders.secret", fallback: "secret") + /// username + internal static let username = Strings.tr("Localizable", "placeholders.username", fallback: "username") + internal enum OnDemand { + /// My SSID + internal static let ssid = Strings.tr("Localizable", "placeholders.on_demand.ssid", fallback: "My SSID") + } + internal enum Profile { + /// My profile + internal static let name = Strings.tr("Localizable", "placeholders.profile.name", fallback: "My profile") + } + } + internal enum Views { + internal enum Advanced { + /// Advanced + internal static let title = Strings.tr("Localizable", "views.advanced.title", fallback: "Advanced") + internal enum Credits { + /// Licenses + internal static let licenses = Strings.tr("Localizable", "views.advanced.credits.licenses", fallback: "Licenses") + /// Notices + internal static let notices = Strings.tr("Localizable", "views.advanced.credits.notices", fallback: "Notices") + /// Credits + internal static let title = Strings.tr("Localizable", "views.advanced.credits.title", fallback: "Credits") + /// Translations + internal static let translations = Strings.tr("Localizable", "views.advanced.credits.translations", fallback: "Translations") + } + internal enum Links { + /// Links + internal static let title = Strings.tr("Localizable", "views.advanced.links.title", fallback: "Links") + internal enum Rows { + /// Disclaimer + internal static let disclaimer = Strings.tr("Localizable", "views.advanced.links.rows.disclaimer", fallback: "Disclaimer") + /// Home page + internal static let homePage = Strings.tr("Localizable", "views.advanced.links.rows.home_page", fallback: "Home page") + /// Join community + internal static let joinCommunity = Strings.tr("Localizable", "views.advanced.links.rows.join_community", fallback: "Join community") + /// Privacy policy + internal static let privacyPolicy = Strings.tr("Localizable", "views.advanced.links.rows.privacy_policy", fallback: "Privacy policy") + /// Write a review + internal static let writeReview = Strings.tr("Localizable", "views.advanced.links.rows.write_review", fallback: "Write a review") + } + internal enum Sections { + /// Support + internal static let support = Strings.tr("Localizable", "views.advanced.links.sections.support", fallback: "Support") + /// Web + internal static let web = Strings.tr("Localizable", "views.advanced.links.sections.web", fallback: "Web") + } + } + internal enum Sections { + /// Resources + internal static let resources = Strings.tr("Localizable", "views.advanced.sections.resources", fallback: "Resources") + } + } + internal enum Diagnostics { + /// Diagnostics + internal static let title = Strings.tr("Localizable", "views.diagnostics.title", fallback: "Diagnostics") + internal enum Alerts { + internal enum ReportIssue { + /// The device is not configured to send e-mails. + internal static let email = Strings.tr("Localizable", "views.diagnostics.alerts.report_issue.email", fallback: "The device is not configured to send e-mails.") + } + } + internal enum Openvpn { + internal enum Rows { + /// Server configuration + internal static let serverConfiguration = Strings.tr("Localizable", "views.diagnostics.openvpn.rows.server_configuration", fallback: "Server configuration") + } + } + internal enum ReportIssue { + /// Report issue + internal static let title = Strings.tr("Localizable", "views.diagnostics.report_issue.title", fallback: "Report issue") + } + internal enum Rows { + /// App + internal static let app = Strings.tr("Localizable", "views.diagnostics.rows.app", fallback: "App") + /// Include private data + internal static let includePrivateData = Strings.tr("Localizable", "views.diagnostics.rows.include_private_data", fallback: "Include private data") + /// Delete all logs + internal static let removeTunnelLogs = Strings.tr("Localizable", "views.diagnostics.rows.remove_tunnel_logs", fallback: "Delete all logs") + /// Tunnel + internal static let tunnel = Strings.tr("Localizable", "views.diagnostics.rows.tunnel", fallback: "Tunnel") + } + internal enum Sections { + /// Live log + internal static let live = Strings.tr("Localizable", "views.diagnostics.sections.live", fallback: "Live log") + /// Tunnel logs + internal static let tunnel = Strings.tr("Localizable", "views.diagnostics.sections.tunnel", fallback: "Tunnel logs") + } + } + internal enum Donate { + /// Make a donation + internal static let title = Strings.tr("Localizable", "views.donate.title", fallback: "Make a donation") + } + internal enum Profile { + internal enum ModuleList { + internal enum Section { + /// Drag modules to rearrange them, as their order determines priority. + internal static let footer = Strings.tr("Localizable", "views.profile.module_list.section.footer", fallback: "Drag modules to rearrange them, as their order determines priority.") + } + } + internal enum Rows { + /// Add module + internal static let addModule = Strings.tr("Localizable", "views.profile.rows.add_module", fallback: "Add module") + } + } + internal enum Profiles { + internal enum Alerts { + internal enum Import { + internal enum Passphrase { + /// Enter passphrase for '%@'. + internal static func message(_ p1: Any) -> String { + return Strings.tr("Localizable", "views.profiles.alerts.import.passphrase.message", String(describing: p1), fallback: "Enter passphrase for '%@'.") + } + /// Decrypt + internal static let ok = Strings.tr("Localizable", "views.profiles.alerts.import.passphrase.ok", fallback: "Decrypt") + } + } + } + internal enum Errors { + /// Unable to duplicate profile '%@'. + internal static func duplicate(_ p1: Any) -> String { + return Strings.tr("Localizable", "views.profiles.errors.duplicate", String(describing: p1), fallback: "Unable to duplicate profile '%@'.") + } + /// Unable to import profiles. + internal static let `import` = Strings.tr("Localizable", "views.profiles.errors.import", fallback: "Unable to import profiles.") + /// Unable to execute tunnel operation. + internal static let tunnel = Strings.tr("Localizable", "views.profiles.errors.tunnel", fallback: "Unable to execute tunnel operation.") + } + internal enum Folders { + /// Installed profile + internal static let activeProfile = Strings.tr("Localizable", "views.profiles.folders.active_profile", fallback: "Installed profile") + /// Add profile + internal static let addProfile = Strings.tr("Localizable", "views.profiles.folders.add_profile", fallback: "Add profile") + /// My profiles + internal static let `default` = Strings.tr("Localizable", "views.profiles.folders.default", fallback: "My profiles") + /// No profiles + internal static let noProfiles = Strings.tr("Localizable", "views.profiles.folders.no_profiles", fallback: "No profiles") + } + internal enum Rows { + /// %d modules + internal static func modules(_ p1: Int) -> String { + return Strings.tr("Localizable", "views.profiles.rows.modules", p1, fallback: "%d modules") + } + /// Select a profile + internal static let notInstalled = Strings.tr("Localizable", "views.profiles.rows.not_installed", fallback: "Select a profile") + } + internal enum Toolbar { + /// Import profile + internal static let importProfile = Strings.tr("Localizable", "views.profiles.toolbar.import_profile", fallback: "Import profile") + /// New profile + internal static let newProfile = Strings.tr("Localizable", "views.profiles.toolbar.new_profile", fallback: "New profile") + } + } + } +} +// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length +// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces + +// MARK: - Implementation Details + +extension Strings { + private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String { + let format = Bundle.main.localizedString(forKey: key, value: value, table: table) + return String(format: format, locale: Locale.current, arguments: args) + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Mock/Mock.swift b/Passepartout/Library/Sources/AppLibrary/Mock/Mock.swift new file mode 100644 index 00000000..3c05ef9f --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Mock/Mock.swift @@ -0,0 +1,133 @@ +// +// Mock.swift +// Passepartout +// +// Created by Davide De Rosa on 6/22/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import AppData +import Combine +import Foundation +import PassepartoutKit +import UtilsLibrary + +// MARK: AppContext + +extension AppContext { + public static let mock: AppContext = .mock(withRegistry: Registry()) + + public static func mock(withRegistry registry: Registry) -> AppContext { + let env = InMemoryEnvironment() + return AppContext( + iapManager: IAPManager( + customUserLevel: nil, + receiptReader: MockReceiptReader(), + productsAtBuild: { _ in + [] + } + ), + profileManager: { + let profiles: [Profile] = (0..<20) + .reduce(into: []) { list, _ in + list.append(.newProfile()) + } + return ProfileManager(profiles: profiles) + }(), + tunnel: Tunnel(strategy: FakeTunnelStrategy(environment: env)), + tunnelEnvironment: env, + registry: registry, + constants: .shared + ) + } +} + +extension IAPManager { + public static var mock: IAPManager { + AppContext.mock.iapManager + } +} + +extension ProfileManager { + public static var mock: ProfileManager { + AppContext.mock.profileManager + } +} + +extension Tunnel { + public static var mock: Tunnel { + AppContext.mock.tunnel + } +} + +extension ConnectionObserver { + public static var mock: ConnectionObserver { + AppContext.mock.connectionObserver + } +} + +// MARK: - Profile + +extension Profile { + static let mock: Profile = { + var profile = Profile.Builder() + profile.name = "Mock profile" + + do { + var ovpn = OpenVPNModule.Builder() + ovpn.configurationBuilder = OpenVPN.Configuration.Builder(withFallbacks: true) + ovpn.configurationBuilder.ca = .init(pem: "some CA") + ovpn.configurationBuilder.remotes = [ + try .init("1.2.3.4", .init(.udp, 80)) + ] + profile.modules.append(try ovpn.tryBuild()) + + var dns = DNSModule.Builder() + dns.protocolType = .https + dns.servers = ["1.1.1.1"] + dns.dohURL = "https://1.1.1.1/dns-query" + profile.modules.append(try dns.tryBuild()) + + var proxy = HTTPProxyModule.Builder() + proxy.address = "1.1.1.1" + proxy.port = 1080 + proxy.secureAddress = "2.2.2.2" + proxy.securePort = 8080 + proxy.bypassDomains = ["bypass.com"] + profile.modules.append(try proxy.tryBuild()) + + profile.activeModulesIds = [ovpn.id, dns.id] + + return try profile.tryBuild() + } catch { + fatalError("Unable to build: \(error)") + } + }() + + static func newProfile() -> Profile { + do { + var copy = mock.builder(withNewId: true) + copy.name = String(copy.id.uuidString.prefix(8)) + return try copy.tryBuild() + } catch { + fatalError("Unable to build: \(error)") + } + } +} diff --git a/Passepartout/App/Context/MockProfileRepository.swift b/Passepartout/Library/Sources/AppLibrary/Mock/MockAppProductHelper.swift similarity index 54% rename from Passepartout/App/Context/MockProfileRepository.swift rename to Passepartout/Library/Sources/AppLibrary/Mock/MockAppProductHelper.swift index 8b7c2fb7..13e2b584 100644 --- a/Passepartout/App/Context/MockProfileRepository.swift +++ b/Passepartout/Library/Sources/AppLibrary/Mock/MockAppProductHelper.swift @@ -1,8 +1,8 @@ // -// MockProfileRepository.swift +// MockAppProductHelper.swift // Passepartout // -// Created by Davide De Rosa on 12/18/23. +// Created by Davide De Rosa on 12/19/23. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,38 +23,35 @@ // along with Passepartout. If not, see . // -import Combine import Foundation -import PassepartoutLibrary +import UtilsLibrary -final class MockProfileRepository: ProfileRepository, ObservableObject { - @Published var profiles: [UUID: Profile] +actor MockAppProductHelper: AppProductHelper { + private(set) var products: [AppProduct: InAppProduct] init() { - profiles = [:] + products = [:] } - func allProfiles() -> [UUID: Profile] { - profiles + nonisolated var canMakePurchases: Bool { + true } - func profile(withId id: UUID) -> Profile? { - profiles[id] - } - - func saveProfiles(_ profiles: [Profile]) throws { - profiles.forEach { - self.profiles[$0.id] = $0 + func fetchProducts() async throws { + products = AppProduct.all.reduce(into: [:]) { + $0[$1] = InAppProduct( + productIdentifier: $1.rawValue, + localizedTitle: $1.rawValue, + localizedPrice: "10.0", + native: $1 + ) } } - func removeProfiles(withIds ids: [UUID]) { - ids.forEach { - profiles.removeValue(forKey: $0) - } + func purchase(productWithIdentifier productIdentifier: AppProduct) async throws -> InAppPurchaseResult { + .done } - func willUpdateProfiles() -> AnyPublisher<[UUID: Profile], Never> { - $profiles.eraseToAnyPublisher() + func restorePurchases() async throws { } } diff --git a/PassepartoutLibrary/Tests/PassepartoutFrontendTests/Mock/MockReceiptReader.swift b/Passepartout/Library/Sources/AppLibrary/Mock/MockAppReceiptReader.swift similarity index 79% rename from PassepartoutLibrary/Tests/PassepartoutFrontendTests/Mock/MockReceiptReader.swift rename to Passepartout/Library/Sources/AppLibrary/Mock/MockAppReceiptReader.swift index 583213c7..f12abbfe 100644 --- a/PassepartoutLibrary/Tests/PassepartoutFrontendTests/Mock/MockReceiptReader.swift +++ b/Passepartout/Library/Sources/AppLibrary/Mock/MockAppReceiptReader.swift @@ -1,5 +1,5 @@ // -// MockReceiptReader.swift +// MockAppReceiptReader.swift // Passepartout // // Created by Davide De Rosa on 12/19/23. @@ -24,17 +24,16 @@ // import Foundation -import PassepartoutCore -@testable import PassepartoutFrontend +import UtilsLibrary -final class MockReceiptReader: ReceiptReader { - var receipt: InAppReceipt? +actor MockReceiptReader: AppReceiptReader { + private var receipt: InAppReceipt? init(receipt: InAppReceipt? = nil) { self.receipt = receipt } - func setReceipt(withBuild build: Int, products: [LocalProduct], cancelledProducts: Set = []) { + func setReceipt(withBuild build: Int, products: [AppProduct], cancelledProducts: Set = []) { receipt = InAppReceipt(originalBuildNumber: build, purchaseReceipts: products.map { .init(productIdentifier: $0.rawValue, cancellationDate: cancelledProducts.contains($0) ? Date() : nil, @@ -42,7 +41,7 @@ final class MockReceiptReader: ReceiptReader { }) } - func receipt(for appType: AppType) -> InAppReceipt? { + func receipt(for userLevel: AppUserLevel) -> InAppReceipt? { receipt } } diff --git a/Passepartout/Library/Sources/AppLibrary/Resources/Credits.json b/Passepartout/Library/Sources/AppLibrary/Resources/Credits.json new file mode 100644 index 00000000..aec3a25f --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Resources/Credits.json @@ -0,0 +1,48 @@ +{ + "author": "Davide De Rosa", + "licenses": [{ + "name": "Kvitto", + "licenseName": "BSD", + "licenseURL": "https://raw.githubusercontent.com/Cocoanetics/Kvitto/develop/LICENSE" + }, { + "name": "lzo", + "licenseName": "GPLv2", + "licenseURL": "https://www.gnu.org/licenses/gpl-2.0.txt" + }, { + "name": "OpenSSL", + "licenseName": "OpenSSL", + "licenseURL": "https://raw.githubusercontent.com/openssl/openssl/master/LICENSE.txt" + }, { + "name": "SwiftGen", + "licenseName": "MIT", + "licenseURL": "https://raw.githubusercontent.com/SwiftGen/SwiftGen/master/LICENCE" + }], + "notices": [{ + "name": "Circle Icons", + "message": "The logo is taken from the awesome Circle Icons set by Nick Roach." + }, { + "name": "Country flags", + "message": "The country flags are taken from: https://github.com/lipis/flag-icon-css/" + }, { + "name": "OpenVPN", + "message": "© Copyright 2024 OpenVPN | OpenVPN is a registered trademark of OpenVPN, Inc." + }, { + "name": "WireGuard", + "message": "© Copyright 2015-2024 Jason A. Donenfeld. All Rights Reserved. \"WireGuard\" and the \"WireGuard\" logo are registered trademarks of Jason A. Donenfeld." + }], + "translations": { + "de": "Christian Lederer, Theodor Tietze", + "el": "Konstantinos Koukoulakis", + "en-US": "Davide De Rosa", + "es": "Davide De Rosa, Elena Vivó", + "fr-FR": "Julien Laniel", + "it": "Davide De Rosa", + "nl": "Norbert de Vreede", + "pl": "Piotr Książek", + "pt-BR": "Helder Santana", + "ru": "Alexander Korobynikov", + "sv": "Henry Gross-Hellsen", + "uk": "Dmitry Chirkin", + "zh-Hans": "OnlyThen" + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Resources/Issue.txt b/Passepartout/Library/Sources/AppLibrary/Resources/Issue.txt new file mode 100644 index 00000000..7791b062 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Resources/Issue.txt @@ -0,0 +1,15 @@ +Hi, + +// enter a description of the issue + +-- + +App: $appLine +OS: $osLine +Device: $deviceLine +Provider: $providerName (last updated: $providerLastUpdate) +Purchased: $purchasedProducts + +-- + +Regards diff --git a/Passepartout/Library/Sources/AppLibrary/Resources/en.lproj/Localizable.strings b/Passepartout/Library/Sources/AppLibrary/Resources/en.lproj/Localizable.strings new file mode 100644 index 00000000..7237b9b6 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Resources/en.lproj/Localizable.strings @@ -0,0 +1,223 @@ +// MARK: Global + +"global.account" = "Account"; +"global.address" = "Address"; +"global.addresses" = "Addresses"; +"global.any" = "Any"; +"global.cancel" = "Cancel"; +"global.certificate" = "Certificate"; +"global.compression" = "Compression"; +"global.connect" = "Connect"; +"global.connection" = "Connection"; +"global.default" = "Default"; +"global.destination" = "Destination"; +"global.disable" = "Disable"; +"global.disabled" = "Disabled"; +"global.disconnect" = "Disconnect"; +"global.domain" = "Domain"; +"global.done" = "Done"; +"global.duplicate" = "Duplicate"; +"global.edit" = "Edit"; +"global.empty" = "Empty"; +"global.enable" = "Enable"; +"global.enabled" = "Enabled"; +"global.endpoint" = "Endpoint"; +"global.folder" = "Folder"; +"global.gateway" = "Gateway"; +"global.general" = "General"; +"global.hostname" = "Hostname"; +"global.interface" = "Interface"; +"global.keep_alive" = "Keep-alive"; +"global.key" = "Key"; +"global.method" = "Method"; +"global.modules" = "Modules"; +"global.n_seconds" = "%d seconds"; +"global.name" = "Name"; +"global.networks" = "Networks"; +"global.no_content" = "No content"; +"global.no_selection" = "No selection"; +"global.none" = "None"; +"global.ok" = "OK"; +"global.on_demand" = "On demand"; +"global.other" = "Other"; +"global.password" = "Password"; +"global.port" = "Port"; +"global.private_key" = "Private key"; +"global.protocol" = "Protocol"; +"global.public_key" = "Public key"; +"global.remove" = "Delete"; +"global.restart" = "Restart"; +"global.route" = "Route"; +"global.routes" = "Routes"; +"global.save" = "Save"; +"global.server" = "Server"; +"global.servers" = "Servers"; +"global.status" = "Status"; +"global.storage" = "Storage"; +"global.subnet" = "Subnet"; +"global.uninstall" = "Uninstall"; +"global.unknown" = "Unknown"; +"global.username" = "Username"; +"global.version" = "Version"; + +// MARK: - Entities + +"entities.dns.servers" = "Servers"; +"entities.dns.search_domains" = "Search domains"; + +"entities.dns_protocol.cleartext" = "Cleartext"; +"entities.dns_protocol.https" = "Over HTTPS"; +"entities.dns_protocol.tls" = "Over TLS"; + +"entities.http_proxy.bypass_domains" = "Bypass domains"; + +"entities.on_demand.policy.any" = "All networks"; +"entities.on_demand.policy.excluding" = "Excluding"; +"entities.on_demand.policy.including" = "Including"; + +"entities.openvpn.compression_algorithm.other" = "Unsupported"; +"entities.openvpn.otp_method.none" = "None"; +"entities.openvpn.otp_method.append" = "Append"; +"entities.openvpn.otp_method.encode" = "Encode"; + +"entities.profile.name.new" = "New profile"; + +"entities.tunnel_status.inactive" = "Inactive"; +"entities.tunnel_status.activating" = "Activating"; +"entities.tunnel_status.active" = "Active"; +"entities.tunnel_status.deactivating" = "Deactivating"; + +"entities.connection_status.disconnected" = "Disconnected"; +"entities.connection_status.connecting" = "Connecting"; +"entities.connection_status.connected" = "Connected"; +"entities.connection_status.disconnecting" = "Disconnecting"; + +// MARK: - Entity placeholders + +"placeholders.profile.name" = "My profile"; +"placeholders.on_demand.ssid" = "My SSID"; +"placeholders.username" = "username"; +"placeholders.secret" = "secret"; + +// MARK: - Views + +"views.profiles.rows.not_installed" = "Select a profile"; +"views.profiles.rows.modules" = "%d modules"; +"views.profiles.folders.active_profile" = "Installed profile"; +"views.profiles.folders.default" = "My profiles"; +"views.profiles.folders.add_profile" = "Add profile"; +"views.profiles.folders.no_profiles" = "No profiles"; +"views.profiles.toolbar.new_profile" = "New profile"; +"views.profiles.toolbar.import_profile" = "Import profile"; +"views.profiles.alerts.import.passphrase.message" = "Enter passphrase for '%@'."; +"views.profiles.alerts.import.passphrase.ok" = "Decrypt"; +"views.profiles.errors.tunnel" = "Unable to execute tunnel operation."; +"views.profiles.errors.duplicate" = "Unable to duplicate profile '%@'."; +"views.profiles.errors.import" = "Unable to import profiles."; + +"views.profile.rows.add_module" = "Add module"; +"views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority."; + +"views.advanced.title" = "Advanced"; +"views.advanced.sections.resources" = "Resources"; + +"views.advanced.links.title" = "Links"; +"views.advanced.links.sections.support" = "Support"; +"views.advanced.links.sections.web" = "Web"; +"views.advanced.links.rows.join_community" = "Join community"; +"views.advanced.links.rows.write_review" = "Write a review"; +"views.advanced.links.rows.home_page" = "Home page"; +"views.advanced.links.rows.disclaimer" = "Disclaimer"; +"views.advanced.links.rows.privacy_policy" = "Privacy policy"; + +"views.advanced.credits.title" = "Credits"; +"views.advanced.credits.licenses" = "Licenses"; +"views.advanced.credits.notices" = "Notices"; +"views.advanced.credits.translations" = "Translations"; + +"views.donate.title" = "Make a donation"; + +"views.diagnostics.title" = "Diagnostics"; +"views.diagnostics.sections.live" = "Live log"; +"views.diagnostics.sections.tunnel" = "Tunnel logs"; +"views.diagnostics.rows.app" = "App"; +"views.diagnostics.rows.tunnel" = "Tunnel"; +"views.diagnostics.rows.include_private_data" = "Include private data"; +"views.diagnostics.rows.remove_tunnel_logs" = "Delete all logs"; + +"views.diagnostics.openvpn.rows.server_configuration" = "Server configuration"; + +"views.diagnostics.report_issue.title" = "Report issue"; +"views.diagnostics.alerts.report_issue.email" = "The device is not configured to send e-mails."; + +// MARK: - Module views + +"modules.dns.servers.add" = "Add address"; +"modules.dns.search_domains.add" = "Add domain"; +"modules.http_proxy.bypass_domains.add" = "Add bypass domain"; + +"modules.ip.routes.include" = "Include route"; +"modules.ip.routes.exclude" = "Exclude route"; +"modules.ip.routes.included" = "Included routes"; +"modules.ip.routes.excluded" = "Excluded routes"; +"modules.ip.routes.add_family" = "Add %@"; + +"modules.on_demand.policy" = "Policy"; +"modules.on_demand.policy.footer" = "Activate the VPN %@."; +"modules.on_demand.policy.footer.any" = "in any network"; +"modules.on_demand.policy.footer.including" = "only in the networks below"; +"modules.on_demand.policy.footer.excluding" = "except in the networks below"; +"modules.on_demand.mobile" = "Mobile"; +"modules.on_demand.ethernet" = "Ethernet"; +"modules.on_demand.ssid.add" = "Add SSID"; + +"modules.openvpn.pull" = "Pull"; +"modules.openvpn.redirect_gateway" = "Redirect gateway"; +"modules.openvpn.credentials" = "Credentials"; +"modules.openvpn.remotes" = "Remotes"; +"modules.openvpn.communication" = "Communication"; +"modules.openvpn.cipher" = "Cipher"; +"modules.openvpn.digest" = "Digest"; +"modules.openvpn.compression" = "Compression"; +"modules.openvpn.compression_framing" = "Framing"; +"modules.openvpn.compression_algorithm" = "Algorithm"; +"modules.openvpn.tls_wrap" = "Wrapping"; +"modules.openvpn.eku" = "Extended verification"; +"modules.openvpn.renegotiation" = "Renegotiation"; +"modules.openvpn.randomize_endpoint" = "Randomize endpoint"; +"modules.openvpn.randomize_hostname" = "Randomize hostname"; +"modules.openvpn.credentials.interactive" = "Interactive"; +"modules.openvpn.credentials.interactive.footer" = "On-demand will be disabled."; +"modules.openvpn.credentials.otp_method.approach.append" = "The OTP will be appended to the password."; +"modules.openvpn.credentials.otp_method.approach.encode" = "The OTP will be encoded in Base64 with the password."; + +"modules.wireguard.interface" = "Interface"; +"modules.wireguard.peer" = "Peer #%d"; +"modules.wireguard.preshared_key" = "Pre-shared key"; +"modules.wireguard.allowed_ips" = "Allowed IPs"; + +// MARK: - Alerts + +"alerts.iap.restricted.title" = "Restricted"; +"alerts.iap.restricted.message" = "The requested feature is unavailable in this build."; + +// MARK: - Errors + +"errors.app.empty_profile_name" = "Profile name is empty."; +"errors.app.malformed_module" = "Module %@ is malformed. %@"; +"errors.app.multiple_connection_modules" = "Only one connection module can be active at a time."; +"errors.app.ip_module_requires_connection" = "IP module can only be enabled together with a connection."; +"errors.app.default" = "Unable to complete operation."; +"errors.app.passepartout.invalid_fields" = "Invalid fields (%@)."; +"errors.app.passepartout.parsing" = "Unable to parse file."; +"errors.app.passepartout.default" = "Unable to complete operation (code=%@)."; + +"errors.tunnel.auth" = "Auth failed"; +"errors.tunnel.compression" = "Compression unsupported"; +"errors.tunnel.dns" = "DNS failed"; +"errors.tunnel.encryption" = "Encryption failed"; +"errors.tunnel.routing" = "Missing routing"; +"errors.tunnel.shutdown" = "Server shutdown"; +"errors.tunnel.timeout" = "Timeout"; +"errors.tunnel.tls" = "TLS failed"; +"errors.tunnel.generic" = "Failed"; diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Advanced/AdvancedRouterView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/AdvancedRouterView.swift new file mode 100644 index 00000000..965df856 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/AdvancedRouterView.swift @@ -0,0 +1,100 @@ +// +// AdvancedRouterView.swift +// Passepartout +// +// Created by Davide De Rosa on 8/22/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import PassepartoutKit +import SwiftUI + +public struct AdvancedRouterView: View { + + @Environment(\.dismiss) + var dismiss + + let tunnel: Tunnel + + @State + var navigationRoute: NavigationRoute? + + public init(tunnel: Tunnel) { + self.tunnel = tunnel + } +} + +extension AdvancedRouterView { + enum NavigationRoute: Hashable { + case donate + + case diagnostics + + case appDebugLog(title: String) + + case tunnelDebugLog(title: String, url: URL?) + + case links + + case credits + } + + @ViewBuilder + func pushDestination(for item: NavigationRoute?) -> some View { + switch item { + case .donate: + DonateView() + + case .diagnostics: + DiagnosticsView() + + case .appDebugLog(let title): + DebugLogView.withApp(parameters: Constants.shared.log) + .navigationTitle(title) + + case .tunnelDebugLog(let title, let url): + if let url { + DebugLogView.withURL(url) + .navigationTitle(title) + } else { + DebugLogView.withTunnel(tunnel, parameters: Constants.shared.log) + .navigationTitle(title) + } + + case .links: + LinksView() + + case .credits: + CreditsView() + + default: + Text(Strings.Global.noSelection) + .themeEmptyMessage() + } + } +} + +#Preview { + AdvancedRouterView( + tunnel: .mock + ) + .environmentObject(Theme()) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Advanced/AdvancedView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/AdvancedView.swift new file mode 100644 index 00000000..5a5acedb --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/AdvancedView.swift @@ -0,0 +1,72 @@ +// +// AdvancedView.swift +// Passepartout +// +// Created by Davide De Rosa on 8/23/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct AdvancedView: View { + let identifiers: Constants.Identifiers + + @Binding + var navigationRoute: AdvancedRouterView.NavigationRoute? + + var body: some View { + listView + .navigationTitle(Strings.Views.Advanced.title) + } +} + +extension AdvancedView { + var donateLink: some View { + navLink(Strings.Views.Donate.title, to: .donate) + } + + var diagnosticsLink: some View { + navLink(Strings.Views.Diagnostics.title, to: .diagnostics) + } + + var linksLink: some View { + navLink(Strings.Views.Advanced.Links.title, to: .links) + } + + var creditsLink: some View { + navLink(Strings.Views.Advanced.Credits.title, to: .credits) + } +} + +private extension AdvancedView { + func navLink(_ title: String, to route: AdvancedRouterView.NavigationRoute) -> some View { + NavigationLink(title, value: route) + } +} + +#Preview { + AdvancedView( + identifiers: Constants.shared.identifiers, + navigationRoute: .constant(nil) + ) +} diff --git a/Passepartout/App/Views/CreditsView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/CreditsView.swift similarity index 59% rename from Passepartout/App/Views/CreditsView.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Advanced/CreditsView.swift index 3c1d4431..dc3c954e 100644 --- a/Passepartout/App/Views/CreditsView.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/CreditsView.swift @@ -2,7 +2,7 @@ // CreditsView.swift // Passepartout // -// Created by Davide De Rosa on 2/19/22. +// Created by Davide De Rosa on 8/27/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,16 +24,25 @@ // import SwiftUI +import UtilsLibrary struct CreditsView: View { var body: some View { GenericCreditsView( - licensesHeader: nil,// L10n.Credits.Sections.Licenses.header, - noticesHeader: nil,// L10n.Credits.Sections.Notices.header, - translationsHeader: L10n.Global.Strings.translations, - licenses: Unlocalized.Credits.licenses, - notices: Unlocalized.Credits.notices, - translations: Unlocalized.Translations.translators - ).navigationTitle(L10n.Credits.title) + credits: Self.credits, + licensesHeader: Strings.Views.Advanced.Credits.licenses, + noticesHeader: Strings.Views.Advanced.Credits.notices, + translationsHeader: Strings.Views.Advanced.Credits.translations, + errorDescription: { + AppError($0) + .localizedDescription + } + ) + .navigationTitle(Strings.Views.Advanced.Credits.title) + .themeForm() } } + +private extension CreditsView { + static let credits = Bundle.module.unsafeDecode(Credits.self, filename: "Credits") +} diff --git a/Passepartout/App/Views/WelcomeView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/DonateView.swift similarity index 82% rename from Passepartout/App/Views/WelcomeView.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Advanced/DonateView.swift index 3b5b547d..46a604b0 100644 --- a/Passepartout/App/Views/WelcomeView.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/DonateView.swift @@ -1,8 +1,8 @@ // -// WelcomeView.swift +// DonateView.swift // Passepartout // -// Created by Davide De Rosa on 4/22/22. +// Created by Davide De Rosa on 8/24/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,9 +25,12 @@ import SwiftUI -struct WelcomeView: View { +// TODO: donations + +struct DonateView: View { var body: some View { - Text(L10n.Profile.Welcome.message) - .themeInformativeTextStyle() + List { + } + .navigationTitle(Strings.Views.Donate.title) } } diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Advanced/LinksView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/LinksView.swift new file mode 100644 index 00000000..120edf99 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/LinksView.swift @@ -0,0 +1,68 @@ +// +// LinksView.swift +// Passepartout +// +// Created by Davide De Rosa on 8/27/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import SwiftUI + +struct LinksView: View { + var body: some View { + Form { + supportSection + webSection + } + .navigationTitle(Strings.Views.Advanced.Links.title) + .themeForm() + } +} + +private extension LinksView { + var constants: Constants { + .shared + } + + var supportSection: some View { + Section { + Link(Strings.Views.Advanced.Links.Rows.joinCommunity, destination: constants.websites.subreddit) + Link(Strings.Views.Advanced.Links.Rows.writeReview, destination: constants.urlForReview) + } header: { + Text(Strings.Views.Advanced.Links.Sections.support) + } + } + + var webSection: some View { + Section { + Link(Strings.Views.Advanced.Links.Rows.homePage, destination: constants.websites.home) + Link(Strings.Unlocalized.faq, destination: constants.websites.faq) + Link(Strings.Views.Advanced.Links.Rows.disclaimer, destination: constants.websites.disclaimer) + Link(Strings.Views.Advanced.Links.Rows.privacyPolicy, destination: constants.websites.privacyPolicy) + } header: { + Text(Strings.Views.Advanced.Links.Sections.web) + } + } +} + +#Preview { + LinksView() +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Advanced/iOS/AdvancedRouterView+iOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/iOS/AdvancedRouterView+iOS.swift new file mode 100644 index 00000000..30e3b589 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/iOS/AdvancedRouterView+iOS.swift @@ -0,0 +1,53 @@ +// +// AdvancedRouterView+iOS.swift +// Passepartout +// +// Created by Davide De Rosa on 8/26/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) + +import CommonLibrary +import SwiftUI + +extension AdvancedRouterView { + public var body: some View { + NavigationStack { + AdvancedView( + identifiers: Constants.shared.identifiers, + navigationRoute: $navigationRoute + ) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button { + dismiss() + } label: { + ThemeImage(.close) + } + } + } + .navigationDestination(for: NavigationRoute.self, destination: pushDestination) + .themeNavigationDetail() + } + } +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Advanced/iOS/AdvancedView+iOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/iOS/AdvancedView+iOS.swift new file mode 100644 index 00000000..8927f9bf --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/iOS/AdvancedView+iOS.swift @@ -0,0 +1,50 @@ +// +// AdvancedView+iOS.swift +// Passepartout +// +// Created by Davide De Rosa on 8/27/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) + +import SwiftUI + +extension AdvancedView { + var listView: some View { + List { + Section { + // TODO: donations +// donateLink + linksLink + creditsLink + } header: { + Text(Strings.Views.Advanced.Sections.resources) + } + Section { + diagnosticsLink + Text(Strings.Global.version) + .withTrailingText(identifiers.versionString) + } + } + } +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Advanced/macOS/AdvancedRouterView+macOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/macOS/AdvancedRouterView+macOS.swift new file mode 100644 index 00000000..2223b103 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/macOS/AdvancedRouterView+macOS.swift @@ -0,0 +1,57 @@ +// +// AdvancedRouterView+macOS.swift +// Passepartout +// +// Created by Davide De Rosa on 8/26/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(macOS) + +import CommonLibrary +import SwiftUI + +extension AdvancedRouterView { + public var body: some View { + NavigationSplitView { + AdvancedView( + identifiers: Constants.shared.identifiers, + navigationRoute: $navigationRoute + ) + } detail: { + NavigationStack { + pushDestination(for: navigationRoute) + .navigationDestination(for: NavigationRoute.self, destination: pushDestination) + } + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button(Strings.Global.ok) { + dismiss() + } + } + } + } + .onLoad { + navigationRoute = .links + } + } +} + +#endif diff --git a/Passepartout/App/Reusable/IntentActivity.swift b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/macOS/AdvancedView+macOS.swift similarity index 65% rename from Passepartout/App/Reusable/IntentActivity.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Advanced/macOS/AdvancedView+macOS.swift index f76e6700..0765a742 100644 --- a/Passepartout/App/Reusable/IntentActivity.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Advanced/macOS/AdvancedView+macOS.swift @@ -1,8 +1,8 @@ // -// IntentActivity.swift +// AdvancedView+macOS.swift // Passepartout // -// Created by Davide De Rosa on 3/12/22. +// Created by Davide De Rosa on 8/27/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,16 +25,22 @@ import SwiftUI -public struct IntentActivity { - public let name: String +#if os(macOS) - public let handler: (NSUserActivity, UserObject) -> Void -} - -extension View { - public func onIntentActivity(_ activity: IntentActivity, object: UserObject) -> some View { - onContinueUserActivity(activity.name) { - activity.handler($0, object) +extension AdvancedView { + var listView: some View { + List(selection: $navigationRoute) { + // TODO: donations +// donateLink + linksLink + creditsLink + diagnosticsLink + } + .safeAreaInset(edge: .bottom) { + Text(identifiers.versionString) + .padding(.bottom) } } } + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/AppCoordinator.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/AppCoordinator.swift new file mode 100644 index 00000000..a5a0e0b0 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/AppCoordinator.swift @@ -0,0 +1,79 @@ +// +// AppCoordinator.swift +// Passepartout +// +// Created by Davide De Rosa on 8/13/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import PassepartoutKit +import SwiftUI + +public struct AppCoordinator: View { + + @Environment(\.horizontalSizeClass) + private var hsClass + + @Environment(\.verticalSizeClass) + private var vsClass + + @AppStorage(AppPreference.profilesLayout.key) + private var layout: ProfilesLayout = .list + + let profileManager: ProfileManager + + let tunnel: Tunnel + + let registry: Registry + + @StateObject + private var profileEditor = ProfileEditor() + + public init( + profileManager: ProfileManager, + tunnel: Tunnel, + registry: Registry + ) { + self.profileManager = profileManager + self.tunnel = tunnel + self.registry = registry + } + + public var body: some View { + if hsClass == .regular && vsClass == .regular { + AppModalCoordinator( + layout: $layout, + profileManager: profileManager, + profileEditor: profileEditor, + tunnel: tunnel, + registry: registry + ) + } else { + AppInlineCoordinator( + layout: $layout, + profileManager: profileManager, + profileEditor: profileEditor, + tunnel: tunnel, + registry: registry + ) + } + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/AppInlineCoordinator.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/AppInlineCoordinator.swift new file mode 100644 index 00000000..aa80b589 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/AppInlineCoordinator.swift @@ -0,0 +1,160 @@ +// +// AppInlineCoordinator.swift +// Passepartout +// +// Created by Davide De Rosa on 8/13/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +@MainActor +struct AppInlineCoordinator: View { + + @Binding + var layout: ProfilesLayout + + let profileManager: ProfileManager + + let profileEditor: ProfileEditor + + let tunnel: Tunnel + + let registry: Registry + + @State + private var path = NavigationPath() + + @State + private var modalRoute: ModalRoute? + + @State + private var isImporting = false + + var body: some View { + NavigationStack(path: $path) { + contentView + .toolbar(content: toolbarContent) + .navigationDestination(for: NavigationRoute.self, destination: pushDestination) + } + .themeModal(item: $modalRoute, isRoot: true, content: modalDestination) + } +} + +// MARK: - Destinations + +private extension AppInlineCoordinator { + enum NavigationRoute: Hashable { + case editProfile + } + + enum ModalRoute: String, Identifiable { + case settings + + var id: [String] { + switch self { + case .settings: + return ["settings"] + } + } + } + + var contentView: some View { + ProfileContainerView( + layout: layout, + profileManager: profileManager, + tunnel: tunnel, + registry: registry, + isImporting: $isImporting, + onEdit: { + guard let profile = profileManager.profile(withId: $0.id) else { + return + } + enterDetail(of: profile) + } + ) + } + + func toolbarContent() -> some ToolbarContent { + AppToolbar( + profileManager: profileManager, + layout: $layout, + isImporting: $isImporting, + onSettings: { + modalRoute = .settings + }, + onNewProfile: enterDetail + ) + } + + @ViewBuilder + func pushDestination(for item: NavigationRoute) -> some View { + switch item { + case .editProfile: + ProfileCoordinator( + profileManager: profileManager, + profileEditor: profileEditor, + moduleViewFactory: DefaultModuleViewFactory(), + modally: false, + path: $path + ) { + path.removeLast() + } + } + } + + @ViewBuilder + func modalDestination(for item: ModalRoute?) -> some View { + switch item { + case .settings: + AdvancedRouterView(tunnel: tunnel) + + default: + EmptyView() + } + } + + func enterDetail(of profile: Profile) { + profileEditor.editProfile(profile) + push(.editProfile) + } + + func push(_ item: NavigationRoute) { + path.append(item) + } +} + +#Preview { + + @State + var layout: ProfilesLayout = .list + + return AppInlineCoordinator( + layout: $layout, + profileManager: .mock, + profileEditor: ProfileEditor(profile: .mock), + tunnel: .mock, + registry: Registry() + ) + .environmentObject(Theme()) + .environmentObject(ConnectionObserver.mock) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/AppModalCoordinator.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/AppModalCoordinator.swift new file mode 100644 index 00000000..a649d30f --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/AppModalCoordinator.swift @@ -0,0 +1,151 @@ +// +// AppModalCoordinator.swift +// Passepartout +// +// Created by Davide De Rosa on 6/19/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +@MainActor +struct AppModalCoordinator: View { + + @Binding + var layout: ProfilesLayout + + let profileManager: ProfileManager + + let profileEditor: ProfileEditor + + let tunnel: Tunnel + + let registry: Registry + + @State + private var modalRoute: ModalRoute? + + @State + private var isImporting = false + + @State + private var profilePath = NavigationPath() + + var body: some View { + NavigationStack { + contentView + .toolbar(content: toolbarContent) + } + .themeModal(item: $modalRoute, isRoot: true, content: modalDestination) + } +} + +// MARK: - Destinations + +extension AppModalCoordinator { + enum ModalRoute: Identifiable { + case editProfile + + case settings + + var id: [String] { + switch self { + case .editProfile: + return ["editProfile"] + + case .settings: + return ["settings"] + } + } + } + + var contentView: some View { + ProfileContainerView( + layout: layout, + profileManager: profileManager, + tunnel: tunnel, + registry: registry, + isImporting: $isImporting, + onEdit: { + guard let profile = profileManager.profile(withId: $0.id) else { + return + } + enterDetail(of: profile) + } + ) + } + + func toolbarContent() -> some ToolbarContent { + AppToolbar( + profileManager: profileManager, + layout: $layout, + isImporting: $isImporting, + onSettings: { + modalRoute = .settings + }, + onNewProfile: enterDetail + ) + } + + @ViewBuilder + func modalDestination(for item: ModalRoute?) -> some View { + switch item { + case .editProfile: + ProfileCoordinator( + profileManager: profileManager, + profileEditor: profileEditor, + moduleViewFactory: DefaultModuleViewFactory(), + modally: true, + path: $profilePath + ) { + modalRoute = nil + } + + case .settings: + AdvancedRouterView(tunnel: tunnel) + + default: + EmptyView() + } + } + + func enterDetail(of profile: Profile) { + profilePath = NavigationPath() + profileEditor.editProfile(profile) + modalRoute = .editProfile + } +} + +#Preview { + + @State + var layout: ProfilesLayout = .grid + + return AppModalCoordinator( + layout: $layout, + profileManager: .mock, + profileEditor: ProfileEditor(profile: .mock), + tunnel: .mock, + registry: Registry() + ) + .environmentObject(Theme()) + .environmentObject(ConnectionObserver.mock) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/AppToolbar.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/AppToolbar.swift new file mode 100644 index 00000000..b62bd10a --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/AppToolbar.swift @@ -0,0 +1,103 @@ +// +// AppToolbar.swift +// Passepartout +// +// Created by Davide De Rosa on 9/7/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +struct AppToolbar: ToolbarContent { + + @Environment(\.horizontalSizeClass) + private var hsClass + + @Environment(\.verticalSizeClass) + private var vsClass + + let profileManager: ProfileManager + + @Binding + var layout: ProfilesLayout + + @Binding + var isImporting: Bool + + let onSettings: () -> Void + + let onNewProfile: (Profile) -> Void + + var body: some ToolbarContent { + if hsClass == .regular && vsClass == .regular { + ToolbarItemGroup { + addProfileMenu + settingsButton + layoutPicker + } + } else { + ToolbarItem(placement: .navigation) { + settingsButton + } + ToolbarItemGroup(placement: .primaryAction) { + addProfileMenu + layoutPicker + } + } + } +} + +private extension AppToolbar { + var addProfileMenu: some View { + AddProfileMenu( + profileManager: profileManager, + isImporting: $isImporting, + onNewProfile: onNewProfile + ) + } + + var settingsButton: some View { + Button(action: onSettings) { + ThemeImage(.advanced) + } + } + + var layoutPicker: some View { + ProfilesLayoutPicker(layout: $layout) + } +} + +#Preview { + NavigationStack { + Text("AppToolbar") + .toolbar { + AppToolbar( + profileManager: .mock, + layout: .constant(.list), + isImporting: .constant(false), + onSettings: {}, + onNewProfile: { _ in} + ) + } + .frame(width: 600, height: 400) + } + .environmentObject(Theme()) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileContainerView.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileContainerView.swift new file mode 100644 index 00000000..c9c21708 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileContainerView.swift @@ -0,0 +1,160 @@ +// +// ProfileContainerView.swift +// Passepartout +// +// Created by Davide De Rosa on 2/16/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Combine +import CommonLibrary +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ProfileContainerView: View, TunnelInstallationProviding { + let layout: ProfilesLayout + + let profileManager: ProfileManager + + let tunnel: Tunnel + + let registry: Registry + + @Binding + var isImporting: Bool + + let onEdit: (ProfileHeader) -> Void + + @StateObject + private var interactiveManager = InteractiveManager() + + @StateObject + private var errorHandler: ErrorHandler = .default() + + var body: some View { + debugChanges() + return innerView + .modifier(ContainerModifier( + profileManager: profileManager + )) + .modifier(ProfileImporterModifier( + profileManager: profileManager, + registry: registry, + isPresented: $isImporting, + errorHandler: errorHandler + )) + .navigationTitle(Strings.Unlocalized.appName) + .themeModal(isPresented: $interactiveManager.isPresented, content: interactiveDestination) + .withErrorHandler(errorHandler) + } +} + +private extension ProfileContainerView { + + @ViewBuilder + var innerView: some View { + switch layout { + case .list: + ProfileListView( + profileManager: profileManager, + tunnel: tunnel, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + onEdit: onEdit + ) + + case .grid: + ProfileGridView( + profileManager: profileManager, + tunnel: tunnel, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + onEdit: onEdit + ) + } + } + + func interactiveDestination() -> some View { + InteractiveView(manager: interactiveManager) { + errorHandler.handle( + $0, + title: Strings.Global.connection, + message: Strings.Views.Profiles.Errors.tunnel + ) + } + } +} + +private struct ContainerModifier: ViewModifier { + + @ObservedObject + var profileManager: ProfileManager + + @State + private var search = "" + + func body(content: Content) -> some View { + debugChanges() + return ZStack { + content + .opacity(profileManager.hasProfiles ? 1.0 : 0.0) + + if !profileManager.hasProfiles { + Text(Strings.Views.Profiles.Folders.noProfiles) + .themeEmptyMessage() + } + } + .searchable(text: $search) + .onChange(of: search) { + profileManager.search(byName: $0) + } + .themeAnimation(on: profileManager.headers, category: .profiles) + } +} + +// MARK: - Previews + +#Preview("List") { + PreviewView(layout: .list) +} + +#Preview("Grid") { + PreviewView(layout: .grid) +} + +private struct PreviewView: View { + let layout: ProfilesLayout + + var body: some View { + NavigationStack { + ProfileContainerView( + layout: layout, + profileManager: .mock, + tunnel: .mock, + registry: Registry(), + isImporting: .constant(false), + onEdit: { _ in } + ) + } + .environmentObject(Theme()) + .environmentObject(ConnectionObserver.mock) + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileGridView.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileGridView.swift new file mode 100644 index 00000000..6a476ec0 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileGridView.swift @@ -0,0 +1,152 @@ +// +// ProfileGridView.swift +// Passepartout +// +// Created by Davide De Rosa on 8/13/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ProfileGridView: View, ProfileManagerProviding, TunnelInstallationProviding { + + @Environment(\.isSearching) + private var isSearching + + @ObservedObject + var profileManager: ProfileManager + + @ObservedObject + var tunnel: Tunnel + + let interactiveManager: InteractiveManager + + let errorHandler: ErrorHandler + + let onEdit: (ProfileHeader) -> Void + + @State + private var nextProfileId: Profile.ID? + + private let columns: [GridItem] = [GridItem(.adaptive(minimum: 300.0))] + + var body: some View { + debugChanges() + return ScrollViewReader { scrollProxy in + ScrollView { + VStack(spacing: .zero) { + if !isSearching { + headerView(scrollProxy: scrollProxy) + .padding(.bottom) + } + LazyVGrid(columns: columns) { + ForEach(allHeaders, content: profileView) + .onDelete(perform: removeProfiles) + } + .themeGridHeader(title: Strings.Views.Profiles.Folders.default) + } + .padding(.horizontal) + } + } +#if os(macOS) + .padding(.top) +#endif + } +} + +// MARK: - Subviews + +private extension ProfileGridView { + var allHeaders: [ProfileHeader] { + profileManager.headers + } + + func headerView(scrollProxy: ScrollViewProxy) -> some View { + InstalledProfileView( + layout: .grid, + profileManager: profileManager, + profile: installedProfile, + tunnel: tunnel, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + nextProfileId: $nextProfileId, + flow: .init( + onEditProfile: onEdit + ) + ) + .contextMenu { + installedProfile.map { + ProfileContextMenu( + profileManager: profileManager, + tunnel: tunnel, + header: $0.header(), + interactiveManager: interactiveManager, + errorHandler: errorHandler, + isInstalledProfile: true, + onEdit: onEdit + ) + } + } + } + + func profileView(for header: ProfileHeader) -> some View { + ProfileRowView( + style: .full, + profileManager: profileManager, + tunnel: tunnel, + header: header, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + nextProfileId: $nextProfileId, + withMarker: true, + onEdit: onEdit + ) + .themeGridCell(isSelected: header.id == nextProfileId ?? tunnel.installedProfile?.id) + .contextMenu { + ProfileContextMenu( + profileManager: profileManager, + tunnel: tunnel, + header: header, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + isInstalledProfile: false, + onEdit: onEdit + ) + } + .id(header.id) + } +} + +// MARK: - Previews + +#Preview { + ProfileGridView( + profileManager: .mock, + tunnel: .mock, + interactiveManager: InteractiveManager(), + errorHandler: .default(), + onEdit: { _ in } + ) + .themeWindow(width: 600, height: 300) + .environmentObject(Theme()) + .environmentObject(ConnectionObserver.mock) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileListView.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileListView.swift new file mode 100644 index 00000000..f4367124 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfileListView.swift @@ -0,0 +1,141 @@ +// +// ProfileListView.swift +// Passepartout +// +// Created by Davide De Rosa on 9/3/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ProfileListView: View, ProfileManagerProviding, TunnelInstallationProviding { + + @Environment(\.isSearching) + private var isSearching + + @ObservedObject + var profileManager: ProfileManager + + @ObservedObject + var tunnel: Tunnel + + let interactiveManager: InteractiveManager + + let errorHandler: ErrorHandler + + let onEdit: (ProfileHeader) -> Void + + @State + private var nextProfileId: Profile.ID? + + var body: some View { + debugChanges() + return ScrollViewReader { scrollProxy in + Form { + if !isSearching { + headerView(scrollProxy: scrollProxy) + } + Section { + ForEach(allHeaders, content: profileView) + .onDelete(perform: removeProfiles) + } header: { + Text(Strings.Views.Profiles.Folders.default) + } + } + .themeForm() + } + } +} + +private extension ProfileListView { + var allHeaders: [ProfileHeader] { + profileManager.headers + } + + func headerView(scrollProxy: ScrollViewProxy) -> some View { + InstalledProfileView( + layout: .list, + profileManager: profileManager, + profile: installedProfile, + tunnel: tunnel, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + nextProfileId: $nextProfileId, + flow: .init( + onEditProfile: onEdit + ) + ) + .contextMenu { + installedProfile.map { + ProfileContextMenu( + profileManager: profileManager, + tunnel: tunnel, + header: $0.header(), + interactiveManager: interactiveManager, + errorHandler: errorHandler, + isInstalledProfile: true, + onEdit: onEdit + ) + } + } + } + + func profileView(for header: ProfileHeader) -> some View { + ProfileRowView( + style: .compact, + profileManager: profileManager, + tunnel: tunnel, + header: header, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + nextProfileId: $nextProfileId, + withMarker: true, + onEdit: onEdit + ) + .contextMenu { + ProfileContextMenu( + profileManager: profileManager, + tunnel: tunnel, + header: header, + interactiveManager: interactiveManager, + errorHandler: errorHandler, + isInstalledProfile: false, + onEdit: onEdit + ) + } + .id(header.id) + } +} + +// MARK: - Previews + +#Preview { + ProfileListView( + profileManager: .mock, + tunnel: .mock, + interactiveManager: InteractiveManager(), + errorHandler: .default(), + onEdit: { _ in } + ) + .environmentObject(Theme()) + .environmentObject(ConnectionObserver.mock) +} diff --git a/PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSVPNProtocol.swift b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfilesLayout.swift similarity index 83% rename from PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSVPNProtocol.swift rename to Passepartout/Library/Sources/AppLibrary/Views/App/ProfilesLayout.swift index 9cf40fe5..b84bdb74 100644 --- a/PassepartoutLibrary/Sources/PassepartoutServices/Domain/WSVPNProtocol.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/App/ProfilesLayout.swift @@ -1,8 +1,8 @@ // -// WSVPNProtocol.swift +// ProfilesLayout.swift // Passepartout // -// Created by Davide De Rosa on 3/20/22. +// Created by Davide De Rosa on 9/5/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,8 +25,8 @@ import Foundation -public enum WSVPNProtocol: String, Codable { - case openVPN = "ovpn" +public enum ProfilesLayout: String, RawRepresentable, CaseIterable, Codable { + case list - case wireGuard = "wg" + case grid } diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/DebugLogView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/DebugLogView.swift new file mode 100644 index 00000000..88c04622 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/DebugLogView.swift @@ -0,0 +1,106 @@ +// +// DebugLogView.swift +// Passepartout +// +// Created by Davide De Rosa on 8/31/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +extension DebugLogView { + static func withApp(parameters: Constants.Log) -> DebugLogView { + DebugLogView { + CommonLibrary.currentLog(parameters: parameters) + } + } + + static func withTunnel(_ tunnel: Tunnel, parameters: Constants.Log) -> DebugLogView { + DebugLogView { + await tunnel.currentLog(parameters: parameters) + } + } + + static func withURL(_ url: URL) -> DebugLogView { + DebugLogView { + do { + return try String(contentsOf: url) + .split(separator: "\n") + .map(String.init) + } catch { + return [] + } + } + } +} + +struct DebugLogView: View { + let fetchLines: () async -> [String] + + @State + private(set) var currentLines: [String] = [] + + var body: some View { + ZStack { + if !currentLines.isEmpty { + contentView + } else { + Text(Strings.Global.noContent) + .themeEmptyMessage() + } + } + .toolbar(content: toolbarContent) + .task { + currentLines = await fetchLines() + } + } + + var content: String { + currentLines.joined(separator: "\n") + } +} + +private extension DebugLogView { + + @ViewBuilder + func toolbarContent() -> some View { + copyButton +// if !currentLines.isEmpty { +// shareButton +// } + } + + var copyButton: some View { + Button { + copyToPasteboard(content) + } label: { + ThemeImage(.copy) + } + .disabled(currentLines.isEmpty) + } + + // TODO: share as temporary URL (could enable email) +// var shareButton: some View { +// ShareLink(item: content) +// } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/DiagnosticsView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/DiagnosticsView.swift new file mode 100644 index 00000000..d30f2aee --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/DiagnosticsView.swift @@ -0,0 +1,204 @@ +// +// DiagnosticsView.swift +// Passepartout +// +// Created by Davide De Rosa on 8/24/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import PassepartoutKit +import SwiftUI + +struct DiagnosticsView: View { + struct LogEntry: Identifiable, Equatable { + let date: Date + + let url: URL + + var id: Date { + date + } + } + + @EnvironmentObject + private var theme: Theme + + @EnvironmentObject + private var connectionObserver: ConnectionObserver + + @EnvironmentObject + var iapManager: IAPManager + + @AppStorage(AppPreference.logsPrivateData.key, store: .group) + private var logsPrivateData = false + + var availableTunnelLogs: () -> [LogEntry] = { + CommonLibrary.availableLogs(at: Constants.shared.urlForTunnelLog) + .sorted { + $0.key > $1.key + } + .map { + LogEntry(date: $0, url: $1) + } + } + + @State + private var tunnelLogs: [LogEntry] = [] + + @State + var isPresentingUnableToEmail = false + + private let dateFormatter: DateFormatter = { + let df = DateFormatter() + df.dateFormat = Constants.shared.formats.timestamp + return df + }() + + var body: some View { + Form { + liveLogSection + openVPNSection + tunnelLogsSection + if iapManager.isEligibleForFeedback() { + reportIssueSection + } + } + .onLoad { + tunnelLogs = availableTunnelLogs() + } + .themeForm() + .navigationTitle(Strings.Views.Diagnostics.title) + .alert(Strings.Views.Diagnostics.ReportIssue.title, isPresented: $isPresentingUnableToEmail) { + Button(Strings.Global.ok, role: .cancel) { + isPresentingUnableToEmail = false + } + } message: { + Text(Strings.Views.Diagnostics.Alerts.ReportIssue.email) + } + } +} + +private extension DiagnosticsView { + var liveLogSection: some View { + Section { + navLink(Strings.Views.Diagnostics.Rows.app, to: .appDebugLog(title: Strings.Views.Diagnostics.Rows.app)) + navLink(Strings.Views.Diagnostics.Rows.tunnel, to: .tunnelDebugLog(title: Strings.Views.Diagnostics.Rows.tunnel, url: nil)) + + Toggle(Strings.Views.Diagnostics.Rows.includePrivateData, isOn: $logsPrivateData) + .onChange(of: logsPrivateData) { + PassepartoutConfiguration.shared.logsAddresses = $0 + PassepartoutConfiguration.shared.logsModules = $0 + } + } header: { + Text(Strings.Views.Diagnostics.Sections.live) + } + } + + var tunnelLogsSection: some View { + Section { + Button(Strings.Views.Diagnostics.Rows.removeTunnelLogs) { + withAnimation(theme.animation(for: .diagnostics), removeTunnelLogs) + } + .disabled(tunnelLogs.isEmpty) + + ForEach(tunnelLogs, id: \.date, content: logView) + .onDelete(perform: removeTunnelLogs) + } header: { + Text(Strings.Views.Diagnostics.Sections.tunnel) + } + .themeAnimation(on: tunnelLogs, category: .diagnostics) + } + + var openVPNSection: some View { + connectionObserver.value(forKey: TunnelEnvironmentKeys.OpenVPN.serverConfiguration) + .map { cfg in + Section { + NavigationLink(Strings.Views.Diagnostics.Openvpn.Rows.serverConfiguration) { + OpenVPNView(serverConfiguration: cfg) + .navigationTitle(Strings.Views.Diagnostics.Openvpn.Rows.serverConfiguration) + } + } header: { + Text(Strings.Unlocalized.openVPN) + } + } + } + + var reportIssueSection: some View { + Section { + ReportIssueButton( + tunnel: connectionObserver.tunnel, + title: Strings.Views.Diagnostics.ReportIssue.title, + purchasedProducts: iapManager.purchasedProducts, + isUnableToEmail: $isPresentingUnableToEmail + ) + } + } + + func logView(for item: LogEntry) -> some View { + ThemeRemovableItemRow(isEditing: true) { + let dateString = dateFormatter.string(from: item.date) + navLink(dateString, to: .tunnelDebugLog(title: dateString, url: item.url)) + } removeAction: { + removeTunnelLog(at: item.url) + } + } + + func navLink(_ title: String, to value: AdvancedRouterView.NavigationRoute) -> some View { + NavigationLink(title, value: value) + } +} + +private extension DiagnosticsView { + func removeTunnelLog(at url: URL) { + guard let firstIndex = tunnelLogs.firstIndex(where: { $0.url == url }) else { + return + } + try? FileManager.default.removeItem(at: url) + tunnelLogs.remove(at: firstIndex) + } + + func removeTunnelLogs(at offsets: IndexSet) { + offsets.forEach { + try? FileManager.default.removeItem(at: tunnelLogs[$0].url) + } + tunnelLogs.remove(atOffsets: offsets) + } + + func removeTunnelLogs() { + tunnelLogs.forEach { + try? FileManager.default.removeItem(at: $0.url) + } + tunnelLogs.removeAll() + } +} + +#Preview { + DiagnosticsView { + [ + .init(date: Date(), url: URL(string: "http://one.com")!), + .init(date: Date().addingTimeInterval(-60), url: URL(string: "http://two.com")!), + .init(date: Date().addingTimeInterval(-600), url: URL(string: "http://three.com")!) + ] + } + .environmentObject(Theme()) + .environmentObject(ConnectionObserver.mock) + .environmentObject(IAPManager.mock) +} diff --git a/Passepartout/Launcher/PassepartoutLauncherApp.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/ReportIssueButton.swift similarity index 71% rename from Passepartout/Launcher/PassepartoutLauncherApp.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/ReportIssueButton.swift index 0734c085..f040bebe 100644 --- a/Passepartout/Launcher/PassepartoutLauncherApp.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/ReportIssueButton.swift @@ -1,8 +1,8 @@ // -// PassepartoutLauncherApp.swift +// ReportIssueButton.swift // Passepartout // -// Created by Davide De Rosa on 6/25/22. +// Created by Davide De Rosa on 9/18/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,15 +23,22 @@ // along with Passepartout. If not, see . // +import PassepartoutKit import SwiftUI -@main -struct PassepartoutLauncherApp: App { - @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate +struct ReportIssueButton { + let tunnel: Tunnel - var body: some Scene { - WindowGroup { - EmptyView() - } - } + let title: String + + let purchasedProducts: Set + + @Binding + var isUnableToEmail: Bool + + @State + var isPending = false + + @State + var issueBeingReported: Issue? } diff --git a/Passepartout/App/Views/MainView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/iOS/DebugLogView+iOS.swift similarity index 78% rename from Passepartout/App/Views/MainView.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/iOS/DebugLogView+iOS.swift index 0d9dd7cd..31ad478d 100644 --- a/Passepartout/App/Views/MainView.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/iOS/DebugLogView+iOS.swift @@ -1,8 +1,8 @@ // -// MainView.swift +// DebugLogView+iOS.swift // Passepartout // -// Created by Davide De Rosa on 2/8/22. +// Created by Davide De Rosa on 8/31/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,15 +23,16 @@ // along with Passepartout. If not, see . // -#if !os(tvOS) +#if os(iOS) + import SwiftUI -struct MainView: View { - var body: some View { - NavigationView { - OrganizerView() - ProfileView() - }.themeGlobal() +extension DebugLogView { + var contentView: some View { + TextEditor(text: .constant(content)) + .font(.caption) + .monospaced() } } + #endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift new file mode 100644 index 00000000..51a97625 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/iOS/ReportIssueButton+iOS.swift @@ -0,0 +1,101 @@ +// +// ReportIssueButton+iOS.swift +// Passepartout +// +// Created by Davide De Rosa on 9/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) + +import CommonLibrary +import SwiftUI +import UIKit +import UtilsLibrary + +extension ReportIssueButton: View { + var body: some View { + HStack { + Button(title) { + Task { + isPending = true + defer { + isPending = false + } + let issue = await Issue.fromBundle(.main, purchasedProducts: purchasedProducts, tunnel: tunnel) + guard MailComposerView.canSendMail() else { + openMailTo(with: issue) + return + } + issueBeingReported = issue + } + } + if isPending { + Spacer() + ProgressView() + } + } + .disabled(isPending) + .themeModal(item: $issueBeingReported) { + MailComposerView( + isPresented: Binding { + issueBeingReported != nil + } set: { + if !$0 { + issueBeingReported = nil + } + }, + toRecipients: [$0.to], + subject: $0.subject, + messageBody: $0.body, + attachments: $0.attachments + ) + } + } +} + +private extension Issue { + var attachments: [MailComposerView.Attachment] { + var list: [MailComposerView.Attachment] = [] + let mimeType = Strings.Unlocalized.Issues.attachmentMimeType + if let appLog { + list.append(.init(data: appLog, mimeType: mimeType, fileName: Strings.Unlocalized.Issues.appLogFilename)) + } + if let tunnelLog { + list.append(.init(data: tunnelLog, mimeType: mimeType, fileName: Strings.Unlocalized.Issues.tunnelLogFilename)) + } + return list + } +} + +private extension ReportIssueButton { + func openMailTo(with issue: Issue) { + guard let url = URL.mailto(to: issue.to, subject: issue.subject, body: issue.body) else { + return + } + guard UIApplication.shared.canOpenURL(url) else { + isUnableToEmail = true + return + } + UIApplication.shared.open(url) + } +} + +#endif diff --git a/Passepartout/App/Views/TV/MainView+TV.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/macOS/DebugLogView+macOS.swift similarity index 73% rename from Passepartout/App/Views/TV/MainView+TV.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/macOS/DebugLogView+macOS.swift index 6b879195..922ed78d 100644 --- a/Passepartout/App/Views/TV/MainView+TV.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/macOS/DebugLogView+macOS.swift @@ -1,8 +1,8 @@ // -// MainView+TV.swift +// DebugLogView+macOS.swift // Passepartout // -// Created by Davide De Rosa on 12/17/23. +// Created by Davide De Rosa on 8/31/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,21 +23,19 @@ // along with Passepartout. If not, see . // -#if os(tvOS) +#if os(macOS) + import SwiftUI -struct MainView: View { - @State private var path = NavigationPath() - - var body: some View { - NavigationStack(path: $path) { - OrganizerView() +extension DebugLogView { + var contentView: some View { + List { + ForEach(Array(currentLines.enumerated()), id: \.offset) { + Text($0.element) + .monospaced() + } } - .themeGlobal() } } -#Preview { - MainView() -} #endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift new file mode 100644 index 00000000..731633e0 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Diagnostics/macOS/ReportIssueButton+macOS.swift @@ -0,0 +1,62 @@ +// +// ReportIssueButton+macOS.swift +// Passepartout +// +// Created by Davide De Rosa on 9/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(macOS) + +import CommonLibrary +import SwiftUI + +extension ReportIssueButton: View { + var body: some View { + Button(title) { + guard let service = NSSharingService(named: .composeEmail) else { + isUnableToEmail = true + return + } + Task { + let issue = await Issue.fromBundle(.main, purchasedProducts: purchasedProducts, tunnel: tunnel) + service.recipients = [issue.to] + service.subject = issue.subject + service.perform(withItems: issue.items) + } + } + } +} + +private extension Issue { + var items: [Any] { + var list: [Any] = [] + list.append(body) + if let appLog, let url = appLog.toTemporaryURL(withFilename: Strings.Unlocalized.Issues.appLogFilename) { + list.append(url) + } + if let tunnelLog, let url = tunnelLog.toTemporaryURL(withFilename: Strings.Unlocalized.Issues.tunnelLogFilename) { + list.append(url) + } + return list + } +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/DNSView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/DNSView.swift new file mode 100644 index 00000000..aba7ebc0 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/DNSView.swift @@ -0,0 +1,148 @@ +// +// DNSView.swift +// Passepartout +// +// Created by Davide De Rosa on 2/17/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Combine +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +extension DNSModule.Builder: ModuleViewProviding { + func moduleView(with editor: ProfileEditor) -> some View { + DNSView(editor: editor, original: self) + } +} + +private struct DNSView: View { + + @EnvironmentObject + private var theme: Theme + + @ObservedObject + private var editor: ProfileEditor + + @Binding + private var draft: DNSModule.Builder + + init(editor: ProfileEditor, original: DNSModule.Builder) { + self.editor = editor + _draft = editor.binding(forModule: original) + } + + var body: some View { + debugChanges() + return Group { + protocolSection + Group { + domainSection + serversSection + searchDomainsSection + } + .labelsHidden() + } + .themeManualInput() + .asModuleView(with: editor, draft: draft) + } +} + +private extension DNSView { + static let allProtocols: [DNSProtocol] = [ + .cleartext, + .https, + .tls + ] + + var protocolSection: some View { + Section { + Picker(Strings.Global.protocol, selection: $draft.protocolType) { + ForEach(Self.allProtocols, id: \.self) { + Text($0.localizedDescription) + } + } + switch draft.protocolType { + case .cleartext: + EmptyView() + + case .https: + ThemeTextField(Strings.Unlocalized.url, text: $draft.dohURL, placeholder: Strings.Unlocalized.Placeholders.dohURL) + .labelsHidden() + + case .tls: + ThemeTextField(Strings.Global.hostname, text: $draft.dotHostname, placeholder: Strings.Unlocalized.Placeholders.dotHostname) + .labelsHidden() + } + } + } + + var domainSection: some View { + Section { + ThemeTextField(Strings.Global.domain, text: $draft.domainName ?? "", placeholder: Strings.Unlocalized.Placeholders.hostname) + } header: { + Text(Strings.Global.domain) + } + } + + var serversSection: some View { + theme.listSection( + Strings.Entities.Dns.servers, + addTitle: Strings.Modules.Dns.Servers.add, + originalItems: $draft.servers, + itemLabel: { + if $0 { + Text($1.wrappedValue) + } else { + ThemeTextField("", text: $1, placeholder: Strings.Unlocalized.Placeholders.ipV4DNS) + } + } + ) + } + + var searchDomainsSection: some View { + theme.listSection( + Strings.Entities.Dns.searchDomains, + addTitle: Strings.Modules.Dns.SearchDomains.add, + originalItems: $draft.searchDomains ?? [], + itemLabel: { + if $0 { + Text($1.wrappedValue) + } else { + ThemeTextField("", text: $1, placeholder: Strings.Unlocalized.Placeholders.hostname) + } + } + ) + } +} + +// MARK: - Previews + +#Preview { + var module = DNSModule.Builder() + module.protocolType = .https + module.servers = ["1.1.1.1", "2.2.2.2", "3.3.3.3"] + module.dohURL = "https://doh.com/query" + module.dotHostname = "tls.com" + module.domainName = "domain.com" + module.searchDomains = ["one.com", "two.net", "three.com"] + return module.preview() +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/HTTPProxyView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/HTTPProxyView.swift new file mode 100644 index 00000000..ac0fb51a --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/HTTPProxyView.swift @@ -0,0 +1,118 @@ +// +// HTTPProxyView.swift +// Passepartout +// +// Created by Davide De Rosa on 2/17/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +extension HTTPProxyModule.Builder: ModuleViewProviding { + func moduleView(with editor: ProfileEditor) -> some View { + HTTPProxyView(editor: editor, original: self) + } +} + +private struct HTTPProxyView: View { + + @EnvironmentObject + private var theme: Theme + + @ObservedObject + private var editor: ProfileEditor + + @Binding + private var draft: HTTPProxyModule.Builder + + init(editor: ProfileEditor, original: HTTPProxyModule.Builder) { + self.editor = editor + _draft = editor.binding(forModule: original) + } + + var body: some View { + Group { + httpSection + httpsSection + pacSection + bypassSection + } + .labelsHidden() + .themeManualInput() + .asModuleView(with: editor, draft: draft) + } +} + +private extension HTTPProxyView { + var httpSection: some View { + Section { + ThemeTextField(Strings.Global.address, text: $draft.address, placeholder: Strings.Unlocalized.Placeholders.proxyIPv4Address) + ThemeTextField(Strings.Global.port, text: $draft.port.toString(omittingZero: true), placeholder: Strings.Unlocalized.Placeholders.proxyPort) + } header: { + Text(Strings.Unlocalized.http) + } + } + + var httpsSection: some View { + Section { + ThemeTextField(Strings.Global.address, text: $draft.secureAddress, placeholder: Strings.Unlocalized.Placeholders.proxyIPv4Address) + ThemeTextField(Strings.Global.port, text: $draft.securePort.toString(omittingZero: true), placeholder: Strings.Unlocalized.Placeholders.proxyPort) + } header: { + Text(Strings.Unlocalized.https) + } + } + + var pacSection: some View { + Section { + ThemeTextField(Strings.Unlocalized.url, text: $draft.pacURLString, placeholder: Strings.Unlocalized.Placeholders.pacURL) + } header: { + Text(Strings.Unlocalized.pac) + } + } + + @ViewBuilder + var bypassSection: some View { + theme.listSection( + Strings.Entities.HttpProxy.bypassDomains, + addTitle: Strings.Modules.HttpProxy.BypassDomains.add, + originalItems: $draft.bypassDomains, + itemLabel: { + if $0 { + Text($1.wrappedValue) + } else { + ThemeTextField("", text: $1, placeholder: Strings.Unlocalized.Placeholders.hostname) + } + } + ) + } +} + +#Preview { + var module = HTTPProxyModule.Builder() + module.address = "10.10.10.10" + module.port = 1080 + module.secureAddress = "20.20.20.20" + module.securePort = 8080 + module.pacURLString = "http://proxy-pac.url" + module.bypassDomains = ["bypass-one.com", "two-bypass.net"] + return module.preview() +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/IPView+Route.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/IPView+Route.swift new file mode 100644 index 00000000..aa5b7252 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/IPView+Route.swift @@ -0,0 +1,140 @@ +// +// IPView+Route.swift +// Passepartout +// +// Created by Davide De Rosa on 8/20/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +extension IPView { + struct RouteView: View { + + @EnvironmentObject + private var theme: Theme + + let family: Address.Family + + let onSubmit: (Route?) -> Void + + @State + private var destinationString = "" + + @State + private var gatewayString = "" + + @State + private var isDefault = false + + var body: some View { + Form { + Section { + Toggle(Strings.Global.default, isOn: $isDefault.animation(theme.animation(for: .modules))) + } + if !isDefault { + Section { + ThemeTextField(Strings.Global.destination, text: $destinationString, placeholder: Strings.Unlocalized.Placeholders.ipDestination(forFamily: family)) + ThemeTextField(Strings.Global.gateway, text: $gatewayString, placeholder: Strings.Unlocalized.Placeholders.ipGateway(forFamily: family)) + } + } + } + .themeForm() + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button(Strings.Global.cancel, role: .cancel) { + onSubmit(nil) + } + } + ToolbarItem(placement: .confirmationAction) { + Button(Strings.Global.ok, action: parseAndSubmit) + } + } + } + } +} + +private extension IPView.RouteView { + func parseAndSubmit() { + let route: Route + if isDefault { + route = Route(defaultWithGateway: nil) + } else { + guard let destination = Subnet(rawValue: destinationString) else { + return + } + let gateway = Address(rawValue: gatewayString) + guard destination.address.family == family else { + return + } + if let gateway { + guard gateway.family == family else { + return + } + } + route = Route(destination, gateway) + } + onSubmit(route) + } +} + +#Preview { + struct Preview: View { + + @State + private var route: Route? + + @State + private var isPresented = false + + var body: some View { + List { + Button("Add route") { + isPresented = true + } + .sheet(isPresented: $isPresented) { + NavigationStack { + IPView.RouteView(family: .v4) { + route = $0 + isPresented = false + } + .navigationTitle("Add route") + } + } + route.map { route in + VStack { + route.destination.map { + Text("Destination") + .withTrailingText($0.rawValue) + } + route.gateway.map { + Text("Gateway") + .withTrailingText($0.rawValue) + } + } + } + } + } + } + + return Preview() + .environmentObject(Theme()) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/IPView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/IPView.swift new file mode 100644 index 00000000..d4eb5956 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/IPView.swift @@ -0,0 +1,234 @@ +// +// IPView.swift +// Passepartout +// +// Created by Davide De Rosa on 2/17/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +extension IPModule.Builder: ModuleViewProviding { + func moduleView(with editor: ProfileEditor) -> some View { + IPView(editor: editor, original: self) + } +} + +struct IPView: View { + + @ObservedObject + private var editor: ProfileEditor + + @Binding + private var draft: IPModule.Builder + + @State + private var routePresentation: RoutePresentation? + + init(editor: ProfileEditor, original: IPModule.Builder) { + self.editor = editor + _draft = editor.binding(forModule: original) + } + + var body: some View { + Group { + ipSections(for: .v4) + ipSections(for: .v6) + interfaceSection + } + .asModuleView(with: editor, draft: draft) + .themeModal(item: $routePresentation, content: routeModal) + } +} + +private extension IPView { + enum RoutePresentation: Identifiable { + case included(Address.Family) + + case excluded(Address.Family) + + var id: String { + switch self { + case .included(let family): + return "included.\(family)" + + case .excluded(let family): + return "excluded.\(family)" + } + } + + var family: Address.Family { + switch self { + case .included(let family): + return family + + case .excluded(let family): + return family + } + } + + var localizedTitle: String { + switch self { + case .included: + return Strings.Modules.Ip.Routes.include + + case .excluded: + return Strings.Modules.Ip.Routes.exclude + } + } + } + + @ViewBuilder + func ipSections(for family: Address.Family) -> some View { + let ip = binding(forSettingsIn: family) + ForEach(Array(ip.wrappedValue.includedRoutes.enumerated()), id: \.offset) { item in + row(forRoute: item.element) { + ip.wrappedValue.removeIncluded(at: IndexSet(integer: item.offset)) + } + } + .onDelete { + ip.wrappedValue.removeIncluded(at: $0) + } + .asSectionWithHeader(family.localizedDescription) { + Button(Strings.Modules.Ip.Routes.include) { + routePresentation = .included(family) + } + } + ForEach(Array(ip.wrappedValue.excludedRoutes.enumerated()), id: \.offset) { item in + row(forRoute: item.element) { + ip.wrappedValue.removeExcluded(at: IndexSet(integer: item.offset)) + } + } + .onDelete { + ip.wrappedValue.removeExcluded(at: $0) + } + .asSectionWithTrailingContent { + Button(Strings.Modules.Ip.Routes.exclude) { + routePresentation = .excluded(family) + } + } + } + + func row(forRoute route: Route, removeAction: @escaping () -> Void) -> some View { + ThemeRemovableItemRow(isEditing: true) { + ThemeCopiableText(value: route.localizedDescription) + } removeAction: { + removeAction() + } + } + + var interfaceSection: some View { + Section { + ThemeTextField( + Strings.Unlocalized.mtu, + text: Binding { + draft.mtu?.description ?? "" + } set: { + draft.mtu = Int($0) + }, + placeholder: Strings.Unlocalized.Placeholders.mtu + ) + } header: { + Text(Strings.Global.interface) + } + } +} + +private extension IPView { + func binding(forSettingsIn family: Address.Family) -> Binding { + switch family { + case .v4: + return Binding { + draft.ipv4 ?? IPSettings(subnet: nil) + } set: { + draft.ipv4 = $0 + } + + case .v6: + return Binding { + draft.ipv6 ?? IPSettings(subnet: nil) + } set: { + draft.ipv6 = $0 + } + } + } + + func routeModal(item: RoutePresentation) -> some View { + NavigationStack { + RouteView(family: item.family) { route in + defer { + routePresentation = nil + } + guard let route else { + return + } + switch item { + case .included(let family): + switch family { + case .v4: + if draft.ipv4 == nil { + draft.ipv4 = IPSettings(subnet: nil) + } + draft.ipv4?.include(route) + + case .v6: + if draft.ipv6 == nil { + draft.ipv6 = IPSettings(subnet: nil) + } + draft.ipv6?.include(route) + } + + case .excluded(let family): + switch family { + case .v4: + if draft.ipv4 == nil { + draft.ipv4 = IPSettings(subnet: nil) + } + draft.ipv4?.exclude(route) + + case .v6: + if draft.ipv6 == nil { + draft.ipv6 = IPSettings(subnet: nil) + } + draft.ipv6?.exclude(route) + } + } + } + .navigationTitle(item.localizedTitle) + } + } +} + +#Preview { + var module = IPModule.Builder() + module.ipv4 = IPSettings(subnet: nil) + .including(routes: [ + .init(defaultWithGateway: .ip("1.2.3.4", .v4)), + .init(.init(rawValue: "5.5.0.0/16"), .init(rawValue: "5.5.5.5")) + ]) + module.ipv6 = IPSettings(subnet: nil) + .including(routes: [ + .init(defaultWithGateway: .ip("::4", .v6)), + .init(.init(rawValue: "::1/24"), nil) + ]) + return module.preview() +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/OnDemandView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/OnDemandView.swift new file mode 100644 index 00000000..ad504367 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/OnDemandView.swift @@ -0,0 +1,248 @@ +// +// OnDemandView.swift +// Passepartout +// +// Created by Davide De Rosa on 2/23/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +extension OnDemandModule.Builder: ModuleViewProviding { + func moduleView(with editor: ProfileEditor) -> some View { + OnDemandView(editor: editor, original: self) + } +} + +private struct OnDemandView: View { + + @EnvironmentObject + private var theme: Theme + + @ObservedObject + private var editor: ProfileEditor + + private let wifi: Wifi + + @Binding + private var draft: OnDemandModule.Builder + + init( + editor: ProfileEditor, + original: OnDemandModule.Builder, + observer: WifiObserver? = nil + ) { + self.editor = editor + wifi = Wifi(observer: observer ?? CoreLocationWifiObserver()) + _draft = editor.binding(forModule: original) + } + + var body: some View { + Group { + enabledSection + if draft.isEnabled { + policySection + if draft.policy != .any { + networkSection + wifiSection + } + } + } + .asModuleView(with: editor, draft: draft) + } +} + +private extension OnDemandView { + static let allPolicies: [OnDemandModule.Policy] = [ + .any, + .excluding, + .including + ] + + var enabledSection: some View { + Section { + Toggle(Strings.Global.enabled, isOn: $draft.isEnabled) + } + } + + var policySection: some View { + Picker(Strings.Modules.OnDemand.policy, selection: $draft.policy) { + ForEach(Self.allPolicies, id: \.self) { + Text($0.localizedDescription) + } + } + .themeSectionWithFooter(policyFooterDescription) + } + + var policyFooterDescription: String { + guard draft.isEnabled else { + return "" // better animation than removing footer completely + } + let suffix: String + switch draft.policy { + case .any: + suffix = Strings.Modules.OnDemand.Policy.Footer.any + + case .including, .excluding: + if draft.policy == .including { + suffix = Strings.Modules.OnDemand.Policy.Footer.including + } else { + suffix = Strings.Modules.OnDemand.Policy.Footer.excluding + } + } + return Strings.Modules.OnDemand.Policy.footer(suffix) + } + + var networkSection: some View { + Section { + if Utils.hasCellularData() { + Toggle(Strings.Modules.OnDemand.mobile, isOn: $draft.withMobileNetwork) + } else if Utils.hasEthernet() { + Toggle(Strings.Modules.OnDemand.ethernet, isOn: $draft.withEthernetNetwork) + } + } header: { + Text(Strings.Global.networks) + } + } + + var wifiSection: some View { + theme.listSection( + Strings.Unlocalized.wifi, + addTitle: Strings.Modules.OnDemand.Ssid.add, + originalItems: allSSIDs, + emptyValue: { + do { + return try await wifi.currentSSID() + } catch { + return "" + } + }, + itemLabel: { isEditing, binding in + if isEditing { + Text(binding.wrappedValue) + } else { + HStack { + ThemeTextField("", text: binding, placeholder: Strings.Placeholders.OnDemand.ssid) + .frame(maxWidth: .infinity) + .themeManualInput() + Spacer() + Toggle("", isOn: isSSIDOn(binding.wrappedValue)) + } + .labelsHidden() + } + } + ) + } +} + +private extension OnDemandView { + var allSSIDs: Binding<[String]> { + .init { + Array(draft.withSSIDs.keys) + } set: { newValue in + draft.withSSIDs.forEach { + guard newValue.contains($0.key) else { + draft.withSSIDs.removeValue(forKey: $0.key) + return + } + } + newValue.forEach { + guard draft.withSSIDs[$0] == nil else { + return + } + draft.withSSIDs[$0] = false + } +// print(">>> withSSIDs (allSSIDs): \(withSSIDs)") + } + } + + var onSSIDs: Binding> { + .init { + Set(draft.withSSIDs.filter { + $0.value + }.map(\.key)) + } set: { newValue in + draft.withSSIDs.forEach { + guard newValue.contains($0.key) else { + if draft.withSSIDs[$0.key] != nil { + draft.withSSIDs[$0.key] = false + } else { + draft.withSSIDs.removeValue(forKey: $0.key) + } + return + } + } + newValue.forEach { + guard !(draft.withSSIDs[$0] ?? false) else { + return + } + draft.withSSIDs[$0] = true + } +// print(">>> withSSIDs (onSSIDs): \(withSSIDs)") + } + } + + func isSSIDOn(_ ssid: String) -> Binding { + .init { + draft.withSSIDs[ssid] ?? false + } set: { + draft.withSSIDs[ssid] = $0 + } + } +} + +private extension OnDemandView { + func requestSSID(_ text: Binding) { + Task { @MainActor in + let ssid = try await wifi.currentSSID() + if !draft.withSSIDs.keys.contains(ssid) { + text.wrappedValue = ssid + } + } + } +} + +// MARK: - Previews + +#Preview { + var module = OnDemandModule.Builder() + module.policy = .excluding + module.withMobileNetwork = true + module.withSSIDs = [ + "One": true, + "Two": false, + "Three": false + ] + return module.preview { + OnDemandView( + editor: $0, + original: $1, + observer: MockWifi() + ) + } +} + +private class MockWifi: WifiObserver { + func currentSSID() async throws -> String { + "" + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/OpenVPNView+Credentials.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/OpenVPNView+Credentials.swift new file mode 100644 index 00000000..f1bccf68 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/OpenVPNView+Credentials.swift @@ -0,0 +1,142 @@ +// +// OpenVPNView+Credentials.swift +// Passepartout +// +// Created by Davide De Rosa on 9/1/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +extension OpenVPNView { + struct CredentialsView: View { + + @Binding + var isInteractive: Bool + + @Binding + var credentials: OpenVPN.Credentials? + + var isAuthenticating = false + + @State + private var builder = OpenVPN.Credentials.Builder() + + @State + private var otp = "" + + var body: some View { + Form { + if !isAuthenticating { + interactiveSection + } + inputSection + } + .themeAnimation(on: isInteractive, category: .modules) + .themeManualInput() + .themeForm() + .navigationTitle(Strings.Modules.Openvpn.credentials) + .onLoad { + builder = credentials?.builder() ?? OpenVPN.Credentials.Builder() + } + .onChange(of: builder) { + if isAuthenticating { + credentials = $0.buildForAuthentication(otp: otp) + } else { + credentials = $0.build() + } + } + .onChange(of: otp) { + credentials = builder.buildForAuthentication(otp: $0) + } + } + } +} + +private extension OpenVPNView.CredentialsView { + var otpMethods: [OpenVPN.Credentials.OTPMethod] { + [.none, .append, .encode] + } + + var interactiveSection: some View { + Group { + Toggle(Strings.Modules.Openvpn.Credentials.interactive, isOn: $isInteractive) + + if isInteractive { + Picker(Strings.Unlocalized.otp, selection: $builder.otpMethod) { + ForEach(otpMethods, id: \.self) { + Text($0.localizedDescription(style: .entity)) + } + } + } + } + .themeSectionWithFooter(interactiveFooter) + } + + var interactiveFooter: String? { + if isInteractive { + return [ + Strings.Modules.Openvpn.Credentials.Interactive.footer, + builder.otpMethod.localizedDescription(style: .approachDescription) + ].joined(separator: " ") + } + return nil + } + + var inputSection: some View { + Group { + ThemeTextField(Strings.Global.username, text: $builder.username, placeholder: Strings.Placeholders.username) + .textContentType(.username) + ThemeSecureField(title: Strings.Global.password, text: $builder.password, placeholder: Strings.Placeholders.secret) + .textContentType(.password) + + if isAuthenticating && builder.otpMethod != .none { + ThemeSecureField(title: Strings.Unlocalized.otp, text: $otp, placeholder: Strings.Placeholders.secret) + .textContentType(.oneTimeCode) + } + } + .themeSectionWithFooter(inputFooter) + } + + var inputFooter: String? { + if isAuthenticating { + return builder.otpMethod.localizedDescription(style: .approachDescription) + } + return nil + } +} + +#Preview { + + @State + var credentials: OpenVPN.Credentials? + + @State + var isInteractive = false + + return NavigationStack { + OpenVPNView.CredentialsView( + isInteractive: $isInteractive, + credentials: $credentials + ) + .environmentObject(Theme()) + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/OpenVPNView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/OpenVPNView.swift new file mode 100644 index 00000000..ef7c681f --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/OpenVPNView.swift @@ -0,0 +1,389 @@ +// +// OpenVPNView.swift +// Passepartout +// +// Created by Davide De Rosa on 2/17/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +extension OpenVPNModule.Builder: ModuleViewProviding { + func moduleView(with editor: ProfileEditor) -> some View { + OpenVPNView(editor: editor, original: self) + } +} + +extension OpenVPNModule.Builder: InteractiveViewProviding { + func interactiveView(with editor: ProfileEditor) -> some View { + let draft: Binding = editor.binding(forModule: self) + + return OpenVPNView.CredentialsView( + isInteractive: draft.isInteractive, + credentials: draft.credentials, + isAuthenticating: true + ) + } +} + +struct OpenVPNView: View { + private enum Subroute: Hashable { + case credentials + } + + @ObservedObject + private var editor: ProfileEditor + + private let isServerPushed: Bool + + @Binding + private var draft: OpenVPNModule.Builder + + init(serverConfiguration: OpenVPN.Configuration) { + let module = OpenVPNModule.Builder(configurationBuilder: serverConfiguration.builder()) + editor = ProfileEditor(modules: [module]) + _draft = .constant(module) + isServerPushed = true + } + + init(editor: ProfileEditor, original: OpenVPNModule.Builder) { + self.editor = editor + _draft = editor.binding(forModule: original) + isServerPushed = false + } + + var body: some View { + Group { + if !isServerPushed { + moduleSection(for: pullRows, header: Strings.Modules.Openvpn.pull) + } + moduleSection(for: accountRows, header: Strings.Global.account) + moduleSection(for: remotesRows, header: Strings.Modules.Openvpn.remotes) + moduleSection(for: redirectRows, header: Strings.Modules.Openvpn.redirectGateway) + moduleSection( + for: ipRows(for: configuration.ipv4, routes: configuration.routes4), + header: Strings.Unlocalized.ipv4 + ) + moduleSection( + for: ipRows(for: configuration.ipv6, routes: configuration.routes6), + header: Strings.Unlocalized.ipv6 + ) + moduleSection(for: dnsRows, header: Strings.Unlocalized.dns) + moduleSection(for: proxyRows, header: Strings.Unlocalized.proxy) + moduleSection(for: communicationRows, header: Strings.Modules.Openvpn.communication) + moduleSection(for: compressionRows, header: Strings.Modules.Openvpn.compression) + if !isServerPushed { + moduleSection(for: tlsRows, header: Strings.Unlocalized.tls) + } + moduleSection(for: otherRows, header: Strings.Global.other) + } + .asModuleView(with: editor, draft: draft, withName: !isServerPushed) + .navigationDestination(for: Subroute.self) { route in + switch route { + case .credentials: + CredentialsView( + isInteractive: $draft.isInteractive, + credentials: $draft.credentials + ) + } + } + } +} + +private extension OpenVPNView { + var configuration: OpenVPN.Configuration.Builder { + draft.configurationBuilder + } + + var pullRows: [ModuleRow]? { + configuration.pullMask?.map { + .text(caption: $0.localizedDescription, value: nil) + } + .nilIfEmpty + } + + var accountRows: [ModuleRow]? { + guard configuration.authUserPass == true else { + return nil + } + return [.push(caption: Strings.Modules.Openvpn.credentials, route: HashableRoute(Subroute.credentials))] + } + + var remotesRows: [ModuleRow]? { + configuration.remotes?.map { + .copiableText( + value: "\($0.address.rawValue) → \($0.proto.socketType.rawValue):\($0.proto.port)" + ) + } + .nilIfEmpty + } + + func ipRows(for ip: IPSettings?, routes: [Route]?) -> [ModuleRow]? { + var rows: [ModuleRow] = [] + if let ip { + ip.localizedDescription(optionalStyle: .address).map { + rows.append(.copiableText(caption: Strings.Global.address, value: $0)) + } + ip.localizedDescription(optionalStyle: .defaultGateway).map { + rows.append(.copiableText(caption: Strings.Global.gateway, value: $0)) + } + + ip.includedRoutes + .filter { !$0.isDefault } + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Modules.Ip.Routes.included, + values: $0.map(\.localizedDescription) + )) + } + + ip.excludedRoutes + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Modules.Ip.Routes.excluded, + values: $0.map(\.localizedDescription) + )) + } + } + routes?.forEach { + rows.append(.longContent(caption: Strings.Global.route, value: $0.localizedDescription)) + } + return rows.nilIfEmpty + } + + var redirectRows: [ModuleRow]? { + configuration.routingPolicies? + .compactMap { + switch $0 { + case .IPv4: + return .text(caption: Strings.Unlocalized.ipv4) + + case .IPv6: + return .text(caption: Strings.Unlocalized.ipv6) + + default: + return nil + } + } + .nilIfEmpty + } + + var dnsRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + + configuration.dnsServers? + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Global.servers, + values: $0 + )) + } + + configuration.dnsDomain.map { + rows.append(.copiableText( + caption: Strings.Global.domain, + value: $0 + )) + } + + configuration.searchDomains? + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Entities.Dns.searchDomains, + values: $0 + )) + } + + return rows.nilIfEmpty + } + + var proxyRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + configuration.httpProxy.map { + rows.append(.copiableText( + caption: Strings.Unlocalized.http, + value: $0.rawValue + )) + } + configuration.httpsProxy.map { + rows.append(.copiableText( + caption: Strings.Unlocalized.https, + value: $0.rawValue + )) + } + configuration.proxyAutoConfigurationURL.map { + rows.append(.copiableText( + caption: Strings.Unlocalized.pac, + value: $0.absoluteString + )) + } + configuration.proxyBypassDomains? + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Entities.HttpProxy.bypassDomains, + values: $0 + )) + } + return rows.nilIfEmpty + } + + var communicationRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + configuration.cipher.map { + rows.append(.text(caption: Strings.Modules.Openvpn.cipher, value: $0.localizedDescription)) + } + configuration.digest.map { + rows.append(.text(caption: Strings.Modules.Openvpn.digest, value: $0.localizedDescription)) + } + if let xorMethod = configuration.xorMethod { + rows.append(.longContentPreview( + caption: Strings.Unlocalized.xor, + value: xorMethod.localizedDescription(style: .long), + preview: xorMethod.localizedDescription(style: .short) + )) + } + return rows.nilIfEmpty + } + + var compressionRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + configuration.compressionFraming.map { + rows.append(.text(caption: Strings.Modules.Openvpn.compressionFraming, value: $0.localizedDescription)) + } + configuration.compressionAlgorithm.map { + rows.append(.text(caption: Strings.Modules.Openvpn.compressionAlgorithm, value: $0.localizedDescription)) + } + return rows.nilIfEmpty + } + + var tlsRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + configuration.ca.map { + rows.append(.longContentPreview(caption: Strings.Unlocalized.ca, value: $0.pem, preview: nil)) + } + configuration.clientCertificate.map { + rows.append(.longContentPreview(caption: Strings.Global.certificate, value: $0.pem, preview: nil)) + } + configuration.clientKey.map { + rows.append(.longContentPreview(caption: Strings.Global.key, value: $0.pem, preview: nil)) + } + configuration.tlsWrap.map { + rows.append(.longContentPreview( + caption: Strings.Modules.Openvpn.tlsWrap, + value: $0.key.hexString, + preview: configuration.localizedDescription(style: .tlsWrap) + )) + } + rows.append(.text(caption: Strings.Modules.Openvpn.eku, value: configuration.localizedDescription(style: .eku))) + return rows.nilIfEmpty + } + + var otherRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + configuration.localizedDescription(optionalStyle: .keepAlive).map { + rows.append(.text(caption: Strings.Global.keepAlive, value: $0)) + } + configuration.localizedDescription(optionalStyle: .renegotiatesAfter).map { + rows.append(.text(caption: Strings.Modules.Openvpn.renegotiation, value: $0)) + } + configuration.localizedDescription(optionalStyle: .randomizeEndpoint).map { + rows.append(.text(caption: Strings.Modules.Openvpn.randomizeEndpoint, value: $0)) + } + configuration.localizedDescription(optionalStyle: .randomizeHostnames).map { + rows.append(.text(caption: Strings.Modules.Openvpn.randomizeHostname, value: $0)) + } + return rows.nilIfEmpty + } +} + +private extension OpenVPNView { + func importConfiguration(from url: URL) { + // TODO: import draft from external URL + } +} + +// MARK: - Previews + +// swiftlint: disable force_try +#Preview { + var builder = OpenVPN.Configuration.Builder(withFallbacks: true) + builder.noPullMask = [.proxy] + builder.authUserPass = true + builder.remotes = [ + .init(rawValue: "2.2.2.2:UDP:2222")!, + .init(rawValue: "6.6.6.6:UDP:6666")!, + .init(rawValue: "12.12.12.12:TCP:21212")! + ] + builder.ipv4 = IPSettings(subnet: try! .init("5.5.5.5", 24)) + .including(routes: [ + .init(defaultWithGateway: .ip("120.1.1.1", .v4)), + .init(.init(rawValue: "55.10.20.30/32"), nil) + ]) + .excluding(routes: [ + .init(.init(rawValue: "88.40.30.30/32"), nil), + .init(.init(rawValue: "60.60.60.60/32"), .ip("127.0.0.1", .v4)) + ]) + builder.ipv6 = IPSettings(subnet: try! .init("::5", 24)) + .including(routes: [ + .init(defaultWithGateway: .ip("120::1:1:1", .v6)), + .init(.init(rawValue: "55:10:20::30/128"), nil), + .init(.init(rawValue: "60:60:60::60/128"), .ip("::2", .v6)) + ]) + .excluding(routes: [ + .init(.init(rawValue: "88:40:30::30/32"), nil) + ]) + builder.routingPolicies = [.IPv4, .IPv6] + builder.dnsServers = ["1.2.3.4", "4.5.6.7"] + builder.dnsDomain = "domain.com" + builder.searchDomains = ["search1.com", "search2.com"] + builder.httpProxy = try! .init("10.10.10.10", 1080) + builder.httpsProxy = try! .init("10.10.10.10", 8080) + builder.proxyAutoConfigurationURL = URL(string: "https://hello.pac")! + builder.proxyBypassDomains = ["bypass1.com", "bypass2.com"] + builder.xorMethod = .xormask(mask: .init(Data(hex: "1234"))) + builder.ca = .init(mockPem: "ca-certificate") + builder.clientCertificate = .init(mockPem: "client-certificate") + builder.clientKey = .init(mockPem: "client-key") + builder.tlsWrap = .init(strategy: .auth, key: .init(biData: Data(count: 256))) + builder.keepAliveInterval = 10.0 + builder.renegotiatesAfter = 60.0 + builder.randomizeEndpoint = true + builder.randomizeHostnames = true + + let module = OpenVPNModule.Builder(configurationBuilder: builder) + return module.preview(title: "OpenVPN") +} +// swiftlint: enable force_try + +private extension OpenVPN.CryptoContainer { + init(mockPem: String) { + self.init(pem: """ +-----BEGIN CERTIFICATE----- +\(mockPem) +-----END CERTIFICATE----- +""") + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Modules/WireGuardView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Modules/WireGuardView.swift new file mode 100644 index 00000000..fdb9026f --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Modules/WireGuardView.swift @@ -0,0 +1,170 @@ +// +// WireGuardView.swift +// Passepartout +// +// Created by Davide De Rosa on 7/31/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import PassepartoutWireGuardGo +import SwiftUI + +extension WireGuardModule.Builder: ModuleViewProviding { + func moduleView(with editor: ProfileEditor) -> some View { + WireGuardView(editor: editor, original: self) + } +} + +private struct WireGuardView: View { + + @ObservedObject + private var editor: ProfileEditor + + @Binding + private var draft: WireGuardModule.Builder + + init(editor: ProfileEditor, original: WireGuardModule.Builder) { + self.editor = editor + _draft = editor.binding(forModule: original) + } + + var body: some View { + Group { + moduleSection(for: interfaceRows, header: Strings.Modules.Wireguard.interface) + moduleSection(for: dnsRows, header: Strings.Unlocalized.dns) + ForEach(Array(zip(configuration.peers.indices, configuration.peers)), id: \.1.publicKey) { index, peer in + moduleSection(for: peersRows(for: peer), header: Strings.Modules.Wireguard.peer(index + 1)) + } + } + .asModuleView(with: editor, draft: draft) + } +} + +private extension WireGuardView { + var configuration: WireGuard.Configuration.Builder { + draft.configurationBuilder + } + + var interfaceRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + rows.append(.longContent(caption: Strings.Global.privateKey, value: configuration.interface.privateKey)) + configuration.interface.addresses + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Global.addresses, + values: $0 + )) + } + configuration.interface.mtu.map { + rows.append(.text(caption: Strings.Unlocalized.mtu, value: $0.description)) + } + return rows.nilIfEmpty + } + + var dnsRows: [ModuleRow]? { + var rows: [ModuleRow] = [] + + configuration.interface.dns.servers + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Global.servers, + values: $0 + )) + } + + configuration.interface.dns.domainName.map { + rows.append(.text( + caption: Strings.Global.domain, + value: $0 + )) + } + + configuration.interface.dns.searchDomains? + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Entities.Dns.searchDomains, + values: $0 + )) + } + + return rows.nilIfEmpty + } + + func peersRows(for peer: WireGuard.RemoteInterface.Builder) -> [ModuleRow]? { + var rows: [ModuleRow] = [] + rows.append(.longContent(caption: Strings.Global.publicKey, value: peer.publicKey)) + peer.preSharedKey.map { + rows.append(.longContent(caption: Strings.Modules.Wireguard.presharedKey, value: $0)) + } + peer.endpoint.map { + rows.append(.copiableText(caption: Strings.Global.endpoint, value: $0)) + } + peer.allowedIPs + .nilIfEmpty + .map { + rows.append(.textList( + caption: Strings.Modules.Wireguard.allowedIps, + values: $0 + )) + } + peer.keepAlive.map { + rows.append(.text(caption: Strings.Global.keepAlive, value: TimeInterval($0).localizedDescription(style: .timeString))) + } + return rows.nilIfEmpty + } +} + +private extension WireGuardView { + func importConfiguration(from url: URL) { + // TODO: import draft from external URL + } +} + +// MARK: - Previews + +// swiftlint: disable force_try +#Preview { + let gen = StandardWireGuardKeyGenerator() + + var builder = WireGuard.Configuration.Builder(keyGenerator: gen) + builder.interface.addresses = ["1.1.1.1", "2.2.2.2"] + builder.interface.mtu = 1200 + builder.interface.dns.protocolType = .cleartext + builder.interface.dns.servers = ["8.8.8.8", "4.4.4.4"] + builder.interface.dns.domainName = "domain.com" + builder.interface.dns.searchDomains = ["search1.com", "search2.net"] + + builder.peers = (0..<3).map { _ in + var peer = WireGuard.RemoteInterface.Builder(publicKey: try! gen.publicKey(for: gen.newPrivateKey())) + peer.preSharedKey = gen.newPrivateKey() + peer.allowedIPs = ["1.1.1.1/8", "2.2.2.2/12"] + peer.endpoint = "8.8.8.8:12345" + peer.keepAlive = 30 + return peer + } + + let module = WireGuardModule.Builder(configurationBuilder: builder) + return module.preview() +} +// swiftlint: enable force_try diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Paywall/PaywallModifier.swift b/Passepartout/Library/Sources/AppLibrary/Views/Paywall/PaywallModifier.swift new file mode 100644 index 00000000..443ca526 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Paywall/PaywallModifier.swift @@ -0,0 +1,82 @@ +// +// PaywallModifier.swift +// Passepartout +// +// Created by Davide De Rosa on 9/11/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import SwiftUI + +struct PaywallModifier: ViewModifier { + + @Binding + var reason: PaywallReason? + + @State + private var isPresentingRestricted = false + + @State + private var paywallFeature: AppFeature? + + func body(content: Content) -> some View { + content + .alert( + Strings.Alerts.Iap.Restricted.title, + isPresented: $isPresentingRestricted, + actions: { + Button(Strings.Global.ok) { + reason = nil + isPresentingRestricted = false + } + }, + message: { + Text(Strings.Alerts.Iap.Restricted.message) + } + ) + .themeModal(item: $paywallFeature) { + PaywallView(isPresented: isPresentingPurchase, feature: $0) + } + .onChange(of: reason) { + switch $0 { + case .restricted: + isPresentingRestricted = true + + case .purchase(let feature): + paywallFeature = feature + + default: + break + } + } + } +} + +private extension PaywallModifier { + var isPresentingPurchase: Binding { + Binding { + paywallFeature != nil + } set: { + if !$0 { + paywallFeature = nil + } + } + } +} diff --git a/Passepartout/App/Reusable/GenericVersionView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Paywall/PaywallView.swift similarity index 55% rename from Passepartout/App/Reusable/GenericVersionView.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Paywall/PaywallView.swift index 2c0aafde..e798ea5f 100644 --- a/Passepartout/App/Reusable/GenericVersionView.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Paywall/PaywallView.swift @@ -1,8 +1,8 @@ // -// GenericVersionView.swift +// PaywallView.swift // Passepartout // -// Created by Davide De Rosa on 2/26/22. +// Created by Davide De Rosa on 9/10/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,32 +25,37 @@ import SwiftUI -struct GenericVersionView: View { - let logoName: String +struct PaywallView: View { - let appName: String + @EnvironmentObject + private var iapManager: IAPManager - let versionString: String + @Binding + var isPresented: Bool - let extraString: String? + let feature: AppFeature + // TODO: #424, implement payments var body: some View { - ScrollView { - Image(logoName) - Spacer() - Text(appName) - .font(.largeTitle) - Spacer() - Text(versionString) + Text(String(describing: feature)) + .toolbar(content: toolbarContent) + .navigationTitle("Purchase") + } +} - extraString.map { string in - VStack { - Text(string) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) - .multilineTextAlignment(.center) - .padding() - } +private extension PaywallView { + + @ToolbarContentBuilder + func toolbarContent() -> some ToolbarContent { + ToolbarItem(placement: .cancellationAction) { + Button { + isPresented = false + } label: { + ThemeImage(.close) } } } + +// var purchaseView: some View { +// } } diff --git a/Passepartout/App/Reusable/AddingTextField.swift b/Passepartout/Library/Sources/AppLibrary/Views/Profile/ModuleDetailView.swift similarity index 50% rename from Passepartout/App/Reusable/AddingTextField.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Profile/ModuleDetailView.swift index 3d1947c8..10bb8f28 100644 --- a/Passepartout/App/Reusable/AddingTextField.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Profile/ModuleDetailView.swift @@ -1,8 +1,8 @@ // -// AddingTextField.swift +// ModuleDetailView.swift // Passepartout // -// Created by Davide De Rosa on 2/26/22. +// Created by Davide De Rosa on 2/15/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,50 +23,46 @@ // along with Passepartout. If not, see . // +import PassepartoutKit import SwiftUI -struct AddingTextField: View { - var onAdd: (() -> Void)? +struct ModuleDetailView: View { + let profileEditor: ProfileEditor - var onCommit: (() -> Void)? + let moduleId: UUID? - let textField: (@escaping () -> Void) -> Field - - let addLabel: () -> ActionLabel - - var commitLabel: (() -> ActionLabel)? - - @State private var isAdding = false + let moduleViewFactory: any ModuleViewFactory var body: some View { - HStack { - if isAdding { - textField(doCommit) - Spacer() - if let commitLabel = commitLabel { - Button(action: doCommit, label: commitLabel) - } + debugChanges() + return Group { + if let moduleId { + editorView(forModuleWithId: moduleId) } else { - Button(action: doAdd, label: addLabel) + emptyView } } } } -// MARK: - +private extension ModuleDetailView { -private extension AddingTextField { - func doAdd() { - withAnimation { - onAdd?() - isAdding = true - } + @MainActor + func editorView(forModuleWithId moduleId: UUID) -> some View { + AnyView(moduleViewFactory.view(with: profileEditor, moduleId: moduleId)) } - func doCommit() { - withAnimation { - onCommit?() - isAdding = false - } + var emptyView: some View { + Text(Strings.Global.noSelection) + .themeEmptyMessage() } } + +#Preview { + ModuleDetailView( + profileEditor: ProfileEditor(profile: .mock), + moduleId: Profile.mock.modules.first?.id, + moduleViewFactory: DefaultModuleViewFactory() + ) + .environmentObject(Theme()) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Profile/ProfileCoordinator.swift b/Passepartout/Library/Sources/AppLibrary/Views/Profile/ProfileCoordinator.swift new file mode 100644 index 00000000..c28837ff --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Profile/ProfileCoordinator.swift @@ -0,0 +1,148 @@ +// +// ProfileCoordinator.swift +// Passepartout +// +// Created by Davide De Rosa on 9/3/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ProfileCoordinator: View { + struct Flow { + let onNewModule: (ModuleType) -> Void + + let onCommitEditing: () async throws -> Void + + let onCancelEditing: () -> Void + } + + @EnvironmentObject + private var theme: Theme + + @EnvironmentObject + private var iapManager: IAPManager + + let profileManager: ProfileManager + + let profileEditor: ProfileEditor + + let moduleViewFactory: any ModuleViewFactory + + let modally: Bool + + @Binding + var path: NavigationPath + + let onDismiss: () -> Void + + @State + private var paywallReason: PaywallReason? + + @StateObject + private var errorHandler: ErrorHandler = .default() + + var body: some View { + contentView + .modifier(PaywallModifier(reason: $paywallReason)) + .withErrorHandler(errorHandler) + } +} + +// MARK: - Destinations + +private extension ProfileCoordinator { + var contentView: some View { +#if os(iOS) + ProfileEditView( + profileEditor: profileEditor, + moduleViewFactory: moduleViewFactory, + path: $path, + flow: .init( + onNewModule: onNewModule, + onCommitEditing: onCommitEditing, + onCancelEditing: onCancelEditing + ) + ) + .themeNavigationDetail() + .themeNavigationStack(if: modally, path: $path) +#elseif os(macOS) + ProfileSplitView( + profileEditor: profileEditor, + moduleViewFactory: moduleViewFactory, + flow: .init( + onNewModule: onNewModule, + onCommitEditing: onCommitEditing, + onCancelEditing: onCancelEditing + ) + ) +#endif + } +} + +private extension ProfileCoordinator { + func onNewModule(_ moduleType: ModuleType) { + switch moduleType { + case .onDemand: + paywallReason = iapManager.paywallReason(forFeature: .onDemand) + + default: + paywallReason = iapManager.paywallReason(forFeature: .networkSettings) + } + guard paywallReason == nil else { + return + } + + let module = moduleType.newModule() + withAnimation(theme.animation(for: .modules)) { + profileEditor.saveModule(module, activating: true) + } + } + + func onCommitEditing() async throws { + do { + try await profileEditor.save(to: profileManager) + onDismiss() + } catch { + errorHandler.handle(error, title: Strings.Global.save) + throw error + } + } + + func onCancelEditing() { + onDismiss() + } +} + +// MARK: - Previews + +#Preview { + ProfileCoordinator( + profileManager: .mock, + profileEditor: ProfileEditor(profile: .newProfile()), + moduleViewFactory: DefaultModuleViewFactory(), + modally: false, + path: .constant(NavigationPath()), + onDismiss: {} + ) + .environmentObject(Theme()) +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Profile/iOS/ProfileEditView+iOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Profile/iOS/ProfileEditView+iOS.swift new file mode 100644 index 00000000..095da1bf --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Profile/iOS/ProfileEditView+iOS.swift @@ -0,0 +1,175 @@ +// +// ProfileEditView+iOS.swift +// Passepartout +// +// Created by Davide De Rosa on 6/22/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ProfileEditView: View, Routable { + + @ObservedObject + var profileEditor: ProfileEditor + + let moduleViewFactory: any ModuleViewFactory + + @Binding + var path: NavigationPath + + var flow: ProfileCoordinator.Flow? + + @State + private var malformedModuleIds: [UUID] = [] + + var body: some View { + debugChanges() + return List { + NameSection( + name: $profileEditor.name, + placeholder: Strings.Placeholders.Profile.name + ) + Section { + ForEach(profileEditor.modules, id: \.id, content: moduleRow) + .onMove(perform: moveModules) + .onDelete(perform: removeModules) + + addModuleButton + } header: { + Text(Strings.Global.modules) + } footer: { + Text(Strings.Views.Profile.ModuleList.Section.footer) + } + StorageSection( + uuid: profileEditor.id + ) + } + .toolbar(content: toolbarContent) + .navigationTitle(profileEditor.name) + .navigationBarBackButtonHidden(true) + .navigationDestination(for: NavigationRoute.self, destination: pushDestination) + } +} + +// MARK: - + +private extension ProfileEditView { + + @ToolbarContentBuilder + func toolbarContent() -> some ToolbarContent { + ToolbarItem { + ProfileSaveButton( + title: Strings.Global.save, + errorModuleIds: $malformedModuleIds + ) { + try await flow?.onCommitEditing() + } + } + ToolbarItem(placement: .cancellationAction) { + Button(Strings.Global.cancel, role: .cancel) { + flow?.onCancelEditing() + } + } + } + + func moduleRow(for module: any EditableModule) -> some View { + EditorModuleToggle(profileEditor: profileEditor, module: module) { + Button { + push(.moduleDetail(moduleId: module.id)) + } label: { + HStack { + Text(module.description(inEditor: profileEditor)) + .themeError(malformedModuleIds.contains(module.id)) + Spacer() + } + .contentShape(.rect) + } + } + } + + var addModuleButton: some View { + let moduleTypes = profileEditor.availableModuleTypes.sorted { + $0.localizedDescription < $1.localizedDescription + } + return Menu { + ForEach(moduleTypes) { selectedType in + Button(selectedType.localizedDescription) { + flow?.onNewModule(selectedType) + } + } + } label: { + Text(Strings.Views.Profile.Rows.addModule) +// .frame(maxWidth: .infinity, alignment: .leading) + } + .disabled(moduleTypes.isEmpty) + } +} + +private extension ProfileEditView { + func moveModules(from offsets: IndexSet, to newOffset: Int) { + profileEditor.moveModules(from: offsets, to: newOffset) + } + + func removeModules(at offsets: IndexSet) { + profileEditor.removeModules(at: offsets) + } +} + +// MARK: - Destinations + +private extension ProfileEditView { + enum NavigationRoute: Hashable { + case moduleDetail(moduleId: UUID) + } + + @ViewBuilder + func pushDestination(for item: NavigationRoute) -> some View { + switch item { + case .moduleDetail(let moduleId): + ModuleDetailView( + profileEditor: profileEditor, + moduleId: moduleId, + moduleViewFactory: moduleViewFactory + ) + } + } + + func push(_ item: NavigationRoute) { + path.append(item) + } +} + +#Preview { + NavigationStack { + ProfileEditView( + profileEditor: ProfileEditor(profile: .newProfile()), + moduleViewFactory: DefaultModuleViewFactory(), + path: .constant(NavigationPath()) + ) + } + .environmentObject(Theme()) +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ModuleListView+macOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ModuleListView+macOS.swift new file mode 100644 index 00000000..d604216a --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ModuleListView+macOS.swift @@ -0,0 +1,147 @@ +// +// ModuleListView+macOS.swift +// Passepartout +// +// Created by Davide De Rosa on 6/22/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(macOS) + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ModuleListView: View, Routable { + static let generalModuleId = UUID() + + @ObservedObject + var profileEditor: ProfileEditor + + @Binding + var selectedModuleId: UUID? + + @Binding + var malformedModuleIds: [UUID] + + var flow: ProfileCoordinator.Flow? + + var body: some View { + List(selection: $selectedModuleId) { + Section { + NavigationLink(Strings.Global.general, value: ProfileSplitView.Detail.general) + .tag(Self.generalModuleId) + } + Section { + ForEach(profileEditor.modules, id: \.id) { module in + NavigationLink(value: ProfileSplitView.Detail.module(id: module.id)) { + moduleRow(for: module) + } + } + .onMove(perform: moveModules) + } header: { + if !profileEditor.modules.isEmpty { + Text(Strings.Global.modules) + } + } + } + .onDeleteCommand(perform: removeSelectedModule) + .toolbar(content: toolbarContent) + .navigationTitle(profileEditor.name) + } +} + +private extension ModuleListView { + func moduleRow(for module: any EditableModule) -> some View { + HStack { + Text(module.description(inEditor: profileEditor)) + .themeError(malformedModuleIds.contains(module.id)) + Spacer() + EditorModuleToggle(profileEditor: profileEditor, module: module) { + EmptyView() + } + } + } + + @ViewBuilder + func toolbarContent() -> some View { + addModuleMenu + Button(action: removeSelectedModule) { + ThemeImage(.remove) + } + .disabled(!canRemoveSelectedModule) + } + + var addModuleMenu: some View { + let moduleTypes = profileEditor.availableModuleTypes + return Menu { + ForEach(moduleTypes, id: \.self) { moduleType in + Button(moduleType.localizedDescription) { + flow?.onNewModule(moduleType) + } + } + } label: { + ThemeImage(.add) + } + .disabled(moduleTypes.isEmpty) + } +} + +private extension ModuleListView { + func moveModules(from offsets: IndexSet, to newOffset: Int) { + profileEditor.moveModules(from: offsets, to: newOffset) + // XXX: selection is lost after move, reset as a workaround + selectedModuleId = nil + } + + func removeModules(at offsets: IndexSet) { + profileEditor.removeModules(at: offsets) + } + + var effectiveSelectedModuleId: UUID? { + guard selectedModuleId != Self.generalModuleId else { + return nil + } + return selectedModuleId + } + + var canRemoveSelectedModule: Bool { + effectiveSelectedModuleId != nil + } + + func removeSelectedModule() { + guard let effectiveSelectedModuleId else { + return + } + self.selectedModuleId = nil + profileEditor.removeModule(withId: effectiveSelectedModuleId) + } +} + +#Preview { + ModuleListView( + profileEditor: ProfileEditor(profile: .mock), + selectedModuleId: .constant(nil), + malformedModuleIds: .constant([]) + ) + .environmentObject(Theme()) +} + +#endif diff --git a/Passepartout/App/Views/EndpointView.swift b/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ProfileGeneralView+macOS.swift similarity index 59% rename from Passepartout/App/Views/EndpointView.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ProfileGeneralView+macOS.swift index ae79498a..02f64bcc 100644 --- a/Passepartout/App/Views/EndpointView.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ProfileGeneralView+macOS.swift @@ -1,8 +1,8 @@ // -// EndpointView.swift +// ProfileGeneralView.swift // Passepartout // -// Created by Davide De Rosa on 3/27/22. +// Created by Davide De Rosa on 6/25/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,30 +23,36 @@ // along with Passepartout. If not, see . // -#if !os(tvOS) -import PassepartoutLibrary +#if os(macOS) + import SwiftUI -struct EndpointView: View { - @ObservedObject private var currentProfile: ObservableProfile +struct ProfileGeneralView: View { - init(currentProfile: ObservableProfile) { - self.currentProfile = currentProfile - } + @ObservedObject + var profileEditor: ProfileEditor var body: some View { - switch currentProfile.value.currentVPNProtocol { - case .openVPN: - EndpointView.OpenVPNView( - currentProfile: currentProfile + Form { + NameSection( + name: $profileEditor.name, + placeholder: Strings.Placeholders.Profile.name ) - - case .wireGuard: - EndpointView.WireGuardView( - currentProfile: currentProfile, - isReadonly: true + EmptyView() + .themeSectionWithFooter(Strings.Views.Profile.ModuleList.Section.footer) + StorageSection( + uuid: profileEditor.id ) } + .themeForm() } } + +#Preview { + ProfileGeneralView( + profileEditor: ProfileEditor() + ) + .environmentObject(Theme()) +} + #endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ProfileSplitView+macOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ProfileSplitView+macOS.swift new file mode 100644 index 00000000..de85ef09 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Profile/macOS/ProfileSplitView+macOS.swift @@ -0,0 +1,127 @@ +// +// ProfileSplitView+macOS.swift +// Passepartout +// +// Created by Davide De Rosa on 6/19/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(macOS) + +import PassepartoutKit +import SwiftUI +import UtilsLibrary + +struct ProfileSplitView: View, Routable { + let profileEditor: ProfileEditor + + let moduleViewFactory: any ModuleViewFactory + + var flow: ProfileCoordinator.Flow? + + @State + private var selectedModuleId: UUID? = ModuleListView.generalModuleId + + @State + private var malformedModuleIds: [UUID] = [] + + var body: some View { + debugChanges() + return NavigationSplitView { + ModuleListView( + profileEditor: profileEditor, + selectedModuleId: $selectedModuleId, + malformedModuleIds: $malformedModuleIds, + flow: flow + ) + } detail: { + NavigationStack { + switch selectedModuleId { + case ModuleListView.generalModuleId: + detailView(for: .general) + + default: + detailView(for: .module(id: selectedModuleId)) + } + } + .toolbar(content: toolbarContent) + } + } +} + +// MARK: - + +extension ProfileSplitView { + + @ToolbarContentBuilder + func toolbarContent() -> some ToolbarContent { + ToolbarItem(placement: .cancellationAction) { + Button(Strings.Global.cancel, role: .cancel) { + flow?.onCancelEditing() + } + } + ToolbarItem(placement: .confirmationAction) { + ProfileSaveButton( + title: Strings.Global.save, + errorModuleIds: $malformedModuleIds + ) { + try await flow?.onCommitEditing() + } + } + } +} + +// MARK: - Destinations + +extension ProfileSplitView { + enum Detail: Hashable { + case general + + case module(id: UUID?) + } +} + +private extension ProfileSplitView { + + @ViewBuilder + func detailView(for detail: Detail) -> some View { + switch detail { + case .general: + ProfileGeneralView(profileEditor: profileEditor) + + case .module(let id): + ModuleDetailView( + profileEditor: profileEditor, + moduleId: id, + moduleViewFactory: moduleViewFactory + ) + } + } +} + +#Preview { + ProfileSplitView( + profileEditor: ProfileEditor(profile: .newProfile()), + moduleViewFactory: DefaultModuleViewFactory() + ) + .environmentObject(Theme()) +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/DefaultModuleViewFactory.swift b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/DefaultModuleViewFactory.swift new file mode 100644 index 00000000..b861f3a1 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/DefaultModuleViewFactory.swift @@ -0,0 +1,73 @@ +// +// DefaultModuleViewFactory.swift +// Passepartout +// +// Created by Davide De Rosa on 2/24/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit +import SwiftUI + +final class DefaultModuleViewFactory: ModuleViewFactory { + + @ViewBuilder + func view(with editor: ProfileEditor, moduleId: UUID) -> some View { + let result = editor.moduleViewProvider(withId: moduleId) + if let result { + AnyView(result.provider.moduleView(with: editor)) + .navigationTitle(result.title) + } + } +} + +private extension ProfileEditor { + func moduleViewProvider(withId moduleId: UUID) -> (provider: any ModuleViewProviding, title: String)? { + guard let module = module(withId: moduleId) else { +// assertionFailure("No module with ID \(moduleId)") + return nil + } + guard let provider = module as? any ModuleViewProviding else { + assertionFailure("\(type(of: module)) does not provide a default view") + return nil + } + return (provider, module.typeDescription) + } +} + +extension View { + + @MainActor + func asModuleView(with editor: ProfileEditor, draft: T, withName: Bool = true) -> some View where T: EditableModule, T: Equatable { + Form { + if withName { + NameSection( + name: editor.binding(forNameOf: draft.id), + placeholder: draft.typeDescription + ) + } + self + } + .themeForm() + .themeManualInput() + .themeAnimation(on: draft, category: .modules) + } +} diff --git a/Passepartout/App/Mac/MacBundleDelegate.swift b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/EditableModule+Previews.swift similarity index 58% rename from Passepartout/App/Mac/MacBundleDelegate.swift rename to Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/EditableModule+Previews.swift index 387b48da..19162d91 100644 --- a/Passepartout/App/Mac/MacBundleDelegate.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/EditableModule+Previews.swift @@ -1,8 +1,8 @@ // -// MacBundleDelegate.swift +// EditableModule+Previews.swift // Passepartout // -// Created by Davide De Rosa on 6/25/22. +// Created by Davide De Rosa on 8/19/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -23,32 +23,25 @@ // along with Passepartout. If not, see . // -import Foundation +import PassepartoutKit +import SwiftUI -final class MacBundleDelegate: MacMenuDelegate { - private weak var bundle: MacBundle? +extension EditableModule where Self: ModuleViewProviding { @MainActor - var profileManager: LightProfileManager { - DefaultLightProfileManager() + func preview(title: String = "") -> some View { + NavigationStack { + moduleView(with: ProfileEditor(modules: [self])) + .navigationTitle(title) + } + .environmentObject(Theme()) } @MainActor - var providerManager: LightProviderManager { - DefaultLightProviderManager() - } - - @MainActor - var vpnManager: LightVPNManager { - DefaultLightVPNManager() - } - - @MainActor - var utils: LightUtils { - DefaultLightUtils() - } - - init(bundle: MacBundle?) { - self.bundle = bundle + func preview(with content: (ProfileEditor, Self) -> C) -> some View { + NavigationStack { + content(ProfileEditor(modules: [self]), self) + } + .environmentObject(Theme()) } } diff --git a/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleSection.swift b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleSection.swift new file mode 100644 index 00000000..37043431 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleSection.swift @@ -0,0 +1,135 @@ +// +// ModuleSection.swift +// Passepartout +// +// Created by Davide De Rosa on 8/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import SwiftUI +import UtilsLibrary + +enum ModuleRow: Hashable { + enum CopyOnTap: Int, Hashable, Comparable { + case disabled + + case caption + + case value + + case all + + static func < (lhs: Self, rhs: Self) -> Bool { + lhs.rawValue < rhs.rawValue + } + } + + case text(caption: String, value: String? = nil) + + case textList(caption: String, values: [String]) + + case copiableText(caption: String? = nil, value: String) + + case longContent(caption: String, value: String) + + case longContentPreview(caption: String, value: String, preview: String?) + + case push(caption: String, route: HashableRoute) +} + +struct HashableRoute: Hashable { + let route: any Hashable + + init(_ route: any Hashable) { + self.route = route + } + + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.route.hashValue == rhs.route.hashValue + } + + func hash(into hasher: inout Hasher) { + hasher.combine(route) + } +} + +extension Collection { + var nilIfEmpty: [Element]? { + !isEmpty ? Array(self) : nil + } +} + +extension View { + func moduleSection(for rows: [ModuleRow]?, header: String) -> some View { + rows.map { rows in + Section { + ForEach(rows, id: \.self, content: moduleRowView) + } header: { + Text(header) + } + } + } +} + +private extension View { + + @ViewBuilder + func moduleRowView(for row: ModuleRow) -> some View { + switch row { + case .text(let caption, let value): + Text(caption) + .withTrailingText(value) + + case .textList(let caption, let values): + if !values.isEmpty { + NavigationLink(caption) { + Form { + ForEach(Array(values.enumerated()), id: \.offset) { + Text($0.element) + } + } + .navigationTitle(caption) + .themeForm() + } + } else { + Text(caption) + .withTrailingText(Strings.Global.empty) + } + + case .copiableText(let caption, let value): + ThemeCopiableText(title: caption, value: value) + + case .longContent(let title, let content): + LongContentLink(title, content: .constant(content)) { + Text($0) + .foregroundColor(.secondary) + } + + case .longContentPreview(let title, let content, let preview): + LongContentLink(title, content: .constant(content), preview: preview) { + Text(preview != nil ? $0 : "") + .foregroundColor(.secondary) + } + + case .push(let caption, let route): + NavigationLink(caption, value: route.route) + } + } +} diff --git a/Passepartout/App/Managers/UpgradeManagerStrategy.swift b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleViewFactory.swift similarity index 77% rename from Passepartout/App/Managers/UpgradeManagerStrategy.swift rename to Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleViewFactory.swift index a547f46a..bbb15084 100644 --- a/Passepartout/App/Managers/UpgradeManagerStrategy.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleViewFactory.swift @@ -1,8 +1,8 @@ // -// UpgradeManagerStrategy.swift +// ModuleViewFactory.swift // Passepartout // -// Created by Davide De Rosa on 3/20/22. +// Created by Davide De Rosa on 2/24/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -24,10 +24,11 @@ // import Foundation -import PassepartoutLibrary +import SwiftUI -public protocol UpgradeManagerStrategy { - func migrate(store: KeyValueStore, lastVersion: String?) +protocol ModuleViewFactory: AnyObject { + associatedtype Content: View - func migrateData() + @MainActor + func view(with editor: ProfileEditor, moduleId: UUID) -> Content } diff --git a/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleViewProviding.swift b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleViewProviding.swift new file mode 100644 index 00000000..e4eb3278 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ModuleViewProviding.swift @@ -0,0 +1,40 @@ +// +// ModuleViewProviding.swift +// Passepartout +// +// Created by Davide De Rosa on 2/24/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import SwiftUI + +protocol ModuleViewProviding { + associatedtype Content: View + + @MainActor + func moduleView(with editor: ProfileEditor) -> Content +} + +protocol InteractiveViewProviding { + associatedtype InteractiveContent: View + + @MainActor + func interactiveView(with editor: ProfileEditor) -> InteractiveContent +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ProfileEditor+UI.swift b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ProfileEditor+UI.swift new file mode 100644 index 00000000..e0497b75 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/ProfileEditor/ProfileEditor+UI.swift @@ -0,0 +1,51 @@ +// +// ProfileEditor+UI.swift +// Passepartout +// +// Created by Davide De Rosa on 6/28/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +extension ProfileEditor { + func binding(forNameOf moduleId: UUID) -> Binding { + Binding { [weak self] in + self?.name(forModuleWithId: moduleId) ?? "" + } set: { [weak self] in + self?.setName($0, forModuleWithId: moduleId) + } + } + + func binding(forModule module: T) -> Binding where T: EditableModule { + Binding { [weak self] in + guard let foundModule = self?.module(withId: module.id) else { + fatalError("Module not found in editor: \(module.id)") + } + guard let matchingModule = foundModule as? T else { + fatalError("Type mismatch when binding to editor module: \(type(of: foundModule)) != \(type(of: module))") + } + return matchingModule + } set: { [weak self] in + self?.saveModule($0, activating: false) + } + } +} diff --git a/Passepartout/AppShared/Mac/MacMenu.swift b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+ImageName.swift similarity index 53% rename from Passepartout/AppShared/Mac/MacMenu.swift rename to Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+ImageName.swift index fc0d1574..b575029b 100644 --- a/Passepartout/AppShared/Mac/MacMenu.swift +++ b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+ImageName.swift @@ -1,8 +1,8 @@ // -// MacMenu.swift +// Theme+ImageName.swift // Passepartout // -// Created by Davide De Rosa on 6/25/22. +// Created by Davide De Rosa on 8/28/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,21 +25,35 @@ import Foundation -@MainActor -@objc -public protocol MacMenu { - var delegate: MacMenuDelegate? { get set } - - func install() -} - -@objc -public protocol MacMenuDelegate { - var profileManager: LightProfileManager { get } - - var providerManager: LightProviderManager { get } - - var vpnManager: LightVPNManager { get } - - var utils: LightUtils { get } +extension Theme { + public enum ImageName { + case add + case advanced + case close + case contextDuplicate + case contextRemove + case copy + case disclose + case editableSectionEdit + case editableSectionRemove + case footerAdd + case hide + case info + case marked + case moreDetails + case pending + case profileEdit + case profileImport + case profilesGrid + case profilesList + case remove + case share + case show + case sleeping + case tunnelDisable + case tunnelEnable + case tunnelRestart + case tunnelToggle + case tunnelUninstall + } } diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+UI.swift b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+UI.swift new file mode 100644 index 00000000..94af1603 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+UI.swift @@ -0,0 +1,385 @@ +// +// Theme+UI.swift +// Passepartout +// +// Created by Davide De Rosa on 8/28/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import SwiftUI +import UtilsLibrary + +// MARK: - Modifiers + +struct ThemeWindowModifier: ViewModifier { + let size: CGSize +} + +struct ThemeNavigationDetailModifier: ViewModifier { +} + +struct ThemeFormModifier: ViewModifier { + func body(content: Content) -> some View { + content + .formStyle(.grouped) + } +} + +struct ThemeBooleanModalModifier: ViewModifier where Modal: View { + + @EnvironmentObject + private var theme: Theme + + @Binding + var isPresented: Bool + + let isRoot: Bool + + let isInteractive: Bool + + let modal: () -> Modal + + func body(content: Content) -> some View { + content + .sheet(isPresented: $isPresented) { + modal() + .frame(minWidth: modalSize?.width, minHeight: modalSize?.height) + .interactiveDismissDisabled(!isInteractive) + } + } + + private var modalSize: CGSize? { + isRoot ? theme.rootModalSize : theme.secondaryModalSize + } +} + +struct ThemeItemModalModifier: ViewModifier where Modal: View, T: Identifiable { + + @EnvironmentObject + private var theme: Theme + + @Binding + var item: T? + + let isRoot: Bool + + let isInteractive: Bool + + let modal: (T) -> Modal + + func body(content: Content) -> some View { + content + .sheet(item: $item) { + modal($0) + .frame(minWidth: modalSize?.width, minHeight: modalSize?.height) + .interactiveDismissDisabled(!isInteractive) + } + } + + private var modalSize: CGSize? { + isRoot ? theme.rootModalSize : theme.secondaryModalSize + } +} + +struct ThemePlainButtonModifier: ViewModifier { + let action: () -> Void +} + +struct ThemeManualInputModifier: ViewModifier { +} + +struct ThemeEmptyMessageModifier: ViewModifier { + + @EnvironmentObject + private var theme: Theme + + func body(content: Content) -> some View { + VStack { + Spacer() + content + .font(theme.emptyMessageFont) + .foregroundStyle(theme.emptyMessageColor) + Spacer() + } + } +} + +struct ThemeErrorModifier: ViewModifier { + + @EnvironmentObject + private var theme: Theme + + let isError: Bool + + func body(content: Content) -> some View { + content + .foregroundStyle(isError ? theme.errorColor : theme.titleColor) + } +} + +struct ThemeAnimationModifier: ViewModifier where T: Equatable { + + @EnvironmentObject + private var theme: Theme + + let value: T + + let category: ThemeAnimationCategory + + func body(content: Content) -> some View { + content + .animation(theme.animation(for: category), value: value) + } +} + +struct ThemeSectionWithFooterModifier: ViewModifier { + let footer: String? +} + +struct ThemeGridSectionModifier: ViewModifier { + + @EnvironmentObject + private var theme: Theme + + let title: String? + + func body(content: Content) -> some View { + if let title { + Text(title) + .font(theme.gridHeaderStyle) + .fontWeight(theme.relevantWeight) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.leading) + .padding(.bottom, theme.gridHeaderBottom) + } + content + .padding(.bottom) + .padding(.bottom) + } +} + +struct ThemeGridCellModifier: ViewModifier { + + @EnvironmentObject + private var theme: Theme + + let isSelected: Bool + + func body(content: Content) -> some View { + content + .padding() + .background(isSelected ? theme.gridCellActiveColor : theme.gridCellColor) + .clipShape(.rect(cornerRadius: theme.gridRadius)) + } +} + +struct ThemeHoverListRowModifier: ViewModifier { + func body(content: Content) -> some View { + content + .frame(maxHeight: .infinity) + .listRowInsets(.init()) + } +} + +// MARK: - Views + +public enum ThemeAnimationCategory: CaseIterable { + case profiles + + case profilesLayout + + case modules + + case diagnostics +} + +struct ThemeImage: View { + + @EnvironmentObject + private var theme: Theme + + private let name: Theme.ImageName + + init(_ name: Theme.ImageName) { + self.name = name + } + + var body: some View { + Image(systemName: theme.systemImage(name)) + } +} + +struct ThemeImageLabel: View { + + @EnvironmentObject + private var theme: Theme + + private let title: String + + private let name: Theme.ImageName + + init(_ title: String, _ name: Theme.ImageName) { + self.title = title + self.name = name + } + + var body: some View { + Label { + Text(title) + } icon: { + ThemeImage(name) + } + } +} + +struct ThemeCopiableText: View { + + @EnvironmentObject + private var theme: Theme + + var title: String? + + let value: String + + var body: some View { + HStack { + if let title { + Text(title) + Spacer() + } + Text(value) + .foregroundStyle(title == nil ? theme.titleColor : theme.valueColor) + .themeTruncating() + if title == nil { + Spacer() + } + Button { + copyToPasteboard(value) + } label: { + ThemeImage(.copy) + } + // TODO: #584 menu, necessary to avoid cell selection + .buttonStyle(.borderless) + } + } +} + +struct ThemeTappableText: View { + let title: String + + let action: () -> Void + + var commonView: some View { + Button(action: action) { + Text(title) + .themeTruncating() + } + } +} + +struct ThemeTextField: View { + let title: String? + + @Binding + var text: String + + let placeholder: String + + init(_ title: String, text: Binding, placeholder: String) { + self.title = title + _text = text + self.placeholder = placeholder + } + + @ViewBuilder + var commonView: some View { + if let title { + LabeledContent { + fieldView + } label: { + Text(title) + } + } else { + fieldView + } + } + + private var fieldView: some View { + TextField(title ?? "", text: $text, prompt: Text(placeholder)) + } +} + +struct ThemeSecureField: View { + let title: String? + + @Binding + var text: String + + let placeholder: String + + @ViewBuilder + var commonView: some View { + if let title { + LabeledContent { + fieldView + } label: { + Text(title) + } + } else { + fieldView + } + } + + private var fieldView: some View { + RevealingSecureField(title ?? "", text: $text, prompt: Text(placeholder), imageWidth: 30.0) { + ThemeImage(.hide) + .foregroundStyle(Color.accentColor) + } revealImage: { + ThemeImage(.show) + .foregroundStyle(Color.accentColor) + } + } +} + +struct ThemeRemovableItemRow: View where ItemView: View { + let isEditing: Bool + + @ViewBuilder + let itemView: () -> ItemView + + let removeAction: () -> Void + + var body: some View { + RemovableItemRow( + isEditing: isEditing, + itemView: itemView, + removeView: removeView + ) + } +} + +enum ThemeEditableListSection { + struct RemoveLabel: View { + let action: () -> Void + } + + struct EditLabel: View { + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+iOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+iOS.swift new file mode 100644 index 00000000..378fb622 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+iOS.swift @@ -0,0 +1,120 @@ +// +// Theme+iOS.swift +// Passepartout +// +// Created by Davide De Rosa on 6/19/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(iOS) + +import SwiftUI + +extension Theme { + public convenience init() { + self.init(dummy: ()) + animationCategories = [.profiles, .modules, .diagnostics] + } +} + +// MARK: - Modifiers + +extension ThemeWindowModifier { + func body(content: Content) -> some View { + content + } +} + +extension ThemeNavigationDetailModifier { + func body(content: Content) -> some View { + content + .navigationBarTitleDisplayMode(.inline) + } +} + +extension ThemePlainButtonModifier { + func body(content: Content) -> some View { + Button(action: action) { + content + .frame(maxWidth: .infinity) + .contentShape(.rect) + } + .foregroundStyle(.primary) + } +} + +extension ThemeManualInputModifier { + func body(content: Content) -> some View { + content + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + } +} + +extension ThemeSectionWithFooterModifier { + func body(content: Content) -> some View { + Section { + content + } footer: { + footer.map(Text.init) + } + } +} + +// MARK: - Views + +extension ThemeTappableText { + var body: some View { + commonView + .foregroundStyle(.primary) + } +} + +extension ThemeTextField { + var body: some View { + commonView + } +} + +extension ThemeSecureField { + var body: some View { + commonView + } +} + +extension ThemeRemovableItemRow { + func removeView() -> some View { + EmptyView() + } +} + +extension ThemeEditableListSection.RemoveLabel { + var body: some View { + EmptyView() + } +} + +extension ThemeEditableListSection.EditLabel { + var body: some View { + EmptyView() + } +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+macOS.swift b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+macOS.swift new file mode 100644 index 00000000..ff9ee586 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme+macOS.swift @@ -0,0 +1,137 @@ +// +// Theme+macOS.swift +// Passepartout +// +// Created by Davide De Rosa on 7/31/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +#if os(macOS) + +import SwiftUI + +extension Theme { + public convenience init() { + self.init(dummy: Void()) + rootModalSize = CGSize(width: 700, height: 400) + secondaryModalSize = CGSize(width: 500.0, height: 200.0) + animationCategories = [.profiles, .diagnostics] + } +} + +// MARK: - Modifiers + +extension ThemeWindowModifier { + func body(content: Content) -> some View { + content + .frame(width: size.width, height: size.height) + } +} + +extension ThemeNavigationDetailModifier { + func body(content: Content) -> some View { + content + } +} + +extension ThemePlainButtonModifier { + func body(content: Content) -> some View { + Button(action: action) { + content + .frame(maxWidth: .infinity) + .contentShape(.rect) + } + .buttonStyle(.plain) + .cursor(.hand) + } +} + +extension ThemeManualInputModifier { + func body(content: Content) -> some View { + content + .autocorrectionDisabled() + } +} + +extension ThemeSectionWithFooterModifier { + + @ViewBuilder + func body(content: Content) -> some View { + Section { + content + footer.map { + Text($0) + .foregroundStyle(.secondary) + .font(.callout) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + } +} + +// MARK: - Views + +extension ThemeTappableText { + var body: some View { + commonView + .buttonStyle(.plain) + .cursor(.hand) + } +} + +extension ThemeTextField { + var body: some View { + commonView + .labelsHidden() + } +} + +extension ThemeSecureField { + var body: some View { + commonView + .labelsHidden() + } +} + +extension ThemeRemovableItemRow { + func removeView() -> some View { + Button(action: removeAction) { + ThemeImage(.contextRemove) + } + .buttonStyle(.borderless) + } +} + +extension ThemeEditableListSection.RemoveLabel { + var body: some View { + Button(action: action) { + ThemeImage(.editableSectionRemove) + } + .buttonStyle(.borderless) + } +} + +extension ThemeEditableListSection.EditLabel { + var body: some View { + ThemeImage(.editableSectionEdit) + } +} + +#endif diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme.swift b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme.swift new file mode 100644 index 00000000..f6c5211a --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Theme/Theme.swift @@ -0,0 +1,232 @@ +// +// Theme.swift +// Passepartout +// +// Created by Davide De Rosa on 8/18/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import SwiftUI +import UtilsLibrary + +@MainActor +public final class Theme: ObservableObject { + +// @Published +// private var palette: Palette +// +// public init(palette: Palette) { +// self.palette = palette +// } + + var rootModalSize: CGSize? + + var secondaryModalSize: CGSize? + + var relevantWeight: Font.Weight = .semibold + + var titleColor: Color = .primary + + var valueColor: Color = .secondary + + var gridHeaderStyle: Font = .headline + + var gridRadius: CGFloat = 12.0 + + var gridHeaderBottom: CGFloat = 8.0 + + var gridCellColor: HierarchicalShapeStyle = .quinary + + var gridCellActiveColor: HierarchicalShapeStyle = .quaternary + + var emptyMessageFont: Font = .title + + var emptyMessageColor: Color = .secondary + + var activeColor = Color(red: .zero, green: Double(0xAA) / 255.0, blue: .zero) + + var inactiveColor: Color = .gray + + var pendingColor: Color = .orange + + var errorColor: Color = .red + + private var animation: Animation = .spring + + var animationCategories: Set = Set(ThemeAnimationCategory.allCases) + + var systemImage: (ImageName) -> String = { + switch $0 { + case .add: return "plus" + case .advanced: return "gearshape" + case .close: return "xmark" + case .contextDuplicate: return "doc.on.doc" + case .contextRemove: return "trash" + case .copy: return "doc.on.doc" + case .disclose: return "chevron.down" + case .editableSectionEdit: return "arrow.up.arrow.down" + case .editableSectionRemove: return "trash" + case .footerAdd: return "plus.circle" + case .hide: return "eye.slash" + case .info: return "info.circle" + case .marked: return "checkmark" + case .moreDetails: return "ellipsis.circle" + case .pending: return "clock" + case .profileEdit: return "square.and.pencil" + case .profileImport: return "square.and.arrow.down" + case .profilesGrid: return "square.grid.2x2" + case .profilesList: return "rectangle.grid.1x2" + case .remove: return "minus" + case .share: return "square.and.arrow.up" + case .show: return "eye" + case .sleeping: return "powersleep" + case .tunnelDisable: return "arrow.down" + case .tunnelEnable: return "arrow.up" + case .tunnelRestart: return "arrow.clockwise" + case .tunnelToggle: return "power" + case .tunnelUninstall: return "arrow.uturn.down" + } + } + + init(dummy: Void) { + } + + func animation(for category: ThemeAnimationCategory) -> Animation? { + animationCategories.contains(category) ? animation : nil + } +} + +// MARK: - Modifiers + +extension View { + public func themeWindow(width: CGFloat, height: CGFloat) -> some View { + modifier(ThemeWindowModifier(size: .init(width: width, height: height))) + } + + public func themeNavigationDetail() -> some View { + modifier(ThemeNavigationDetailModifier()) + } + + public func themeForm() -> some View { + modifier(ThemeFormModifier()) + } + + public func themeModal( + isPresented: Binding, + isRoot: Bool = false, + isInteractive: Bool = true, + content: @escaping () -> Content + ) -> some View where Content: View { + modifier(ThemeBooleanModalModifier( + isPresented: isPresented, + isRoot: isRoot, + isInteractive: isInteractive, + modal: content + )) + } + + public func themeModal( + item: Binding, + isRoot: Bool = false, + isInteractive: Bool = true, + content: @escaping (T) -> Content + ) -> some View where Content: View, T: Identifiable { + modifier(ThemeItemModalModifier( + item: item, + isRoot: isRoot, + isInteractive: isInteractive, + modal: content + )) + } + + @ViewBuilder + public func themeNavigationStack(if condition: Bool, path: Binding) -> some View { + if condition { + NavigationStack(path: path) { + self + } + } else { + self + } + } + + public func themePlainButton(action: @escaping () -> Void) -> some View { + modifier(ThemePlainButtonModifier(action: action)) + } + + public func themeManualInput() -> some View { + modifier(ThemeManualInputModifier()) + } + + public func themeTruncating(_ mode: Text.TruncationMode = .middle) -> some View { + lineLimit(1) + .truncationMode(mode) + } + + public func themeEmptyMessage() -> some View { + modifier(ThemeEmptyMessageModifier()) + } + + public func themeError(_ isError: Bool) -> some View { + modifier(ThemeErrorModifier(isError: isError)) + } + + public func themeAnimation(on value: T, category: ThemeAnimationCategory) -> some View where T: Equatable { + modifier(ThemeAnimationModifier(value: value, category: category)) + } + + public func themeSectionWithFooter(_ footer: String?) -> some View { + modifier(ThemeSectionWithFooterModifier(footer: footer)) + } + + public func themeGridHeader(title: String?) -> some View { + modifier(ThemeGridSectionModifier(title: title)) + } + + public func themeGridCell(isSelected: Bool) -> some View { + modifier(ThemeGridCellModifier(isSelected: isSelected)) + } + + public func themeHoverListRow() -> some View { + modifier(ThemeHoverListRowModifier()) + } +} + +// MARK: - Views + +extension Theme { + public func listSection( + _ title: String, + addTitle: String, + originalItems: Binding<[T]>, + emptyValue: (() async -> T)? = nil, + @ViewBuilder itemLabel: @escaping (Bool, Binding) -> ItemView + ) -> some View { + EditableListSection( + title, + addTitle: addTitle, + originalItems: originalItems, + emptyValue: emptyValue, + itemLabel: itemLabel, + removeLabel: ThemeEditableListSection.RemoveLabel.init(action:), + editLabel: ThemeEditableListSection.EditLabel.init + ) + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/Theme/ThemeProviding.swift b/Passepartout/Library/Sources/AppLibrary/Views/Theme/ThemeProviding.swift new file mode 100644 index 00000000..61a0caea --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/Theme/ThemeProviding.swift @@ -0,0 +1,30 @@ +// +// ThemeProviding.swift +// Passepartout +// +// Created by Davide De Rosa on 9/6/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation + +protocol ThemeProviding { + var theme: Theme { get } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/UI/AddProfileMenu.swift b/Passepartout/Library/Sources/AppLibrary/Views/UI/AddProfileMenu.swift new file mode 100644 index 00000000..ee54dfc5 --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/UI/AddProfileMenu.swift @@ -0,0 +1,54 @@ +// +// AddProfileMenu.swift +// Passepartout +// +// Created by Davide De Rosa on 9/3/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +struct AddProfileMenu: View { + let profileManager: ProfileManager + + @Binding + var isImporting: Bool + + let onNewProfile: (Profile) -> Void + + var body: some View { + Menu { + Button { + let profile = profileManager.new(withName: Strings.Entities.Profile.Name.new) + onNewProfile(profile) + } label: { + ThemeImageLabel(Strings.Views.Profiles.Toolbar.newProfile, .profileEdit) + } + Button { + isImporting = true + } label: { + ThemeImageLabel(Strings.Views.Profiles.Toolbar.importProfile, .profileImport) + } + } label: { + ThemeImage(.add) + } + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/UI/ConnectionStatusView.swift b/Passepartout/Library/Sources/AppLibrary/Views/UI/ConnectionStatusView.swift new file mode 100644 index 00000000..41be8bdf --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/UI/ConnectionStatusView.swift @@ -0,0 +1,66 @@ +// +// ConnectionStatusView.swift +// Passepartout +// +// Created by Davide De Rosa on 9/4/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit +import SwiftUI + +struct ConnectionStatusView: View, TunnelContextProviding, ThemeProviding { + + @EnvironmentObject + var theme: Theme + + @EnvironmentObject + var connectionObserver: ConnectionObserver + + @ObservedObject + var tunnel: Tunnel + + var body: some View { + Text(statusDescription) + .foregroundStyle(tunnelStatusColor) + } +} + +private extension ConnectionStatusView { + var statusDescription: String { + if let lastErrorCode = connectionObserver.lastErrorCode { + return lastErrorCode.localizedDescription + } + let status = tunnelConnectionStatus + switch status { + case .active: + if let dataCount = connectionObserver.dataCount { + let down = dataCount.received.descriptionAsDataUnit + let up = dataCount.sent.descriptionAsDataUnit + return "↓\(down) ↑\(up)" + } + + default: + break + } + return status.localizedDescription + } +} diff --git a/Passepartout/Library/Sources/AppLibrary/Views/UI/EditorModuleToggle.swift b/Passepartout/Library/Sources/AppLibrary/Views/UI/EditorModuleToggle.swift new file mode 100644 index 00000000..1bacb89b --- /dev/null +++ b/Passepartout/Library/Sources/AppLibrary/Views/UI/EditorModuleToggle.swift @@ -0,0 +1,51 @@ +// +// EditorModuleToggle.swift +// Passepartout +// +// Created by Davide De Rosa on 9/11/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import PassepartoutKit +import SwiftUI + +struct EditorModuleToggle