diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index d4fc4acc7..75ae2337d 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -7,6 +7,6 @@ set -eo pipefail xcodebuild -workspace Mastodon.xcworkspace \ -scheme Mastodon \ - -destination "platform=iOS Simulator,name=iPhone SE (2nd generation)" \ + -destination "platform=iOS Simulator,name=iPhone SE (3rd generation)" \ clean \ build | xcpretty diff --git a/.github/workflows/main.yml b/.github/workflows/build-only.yml similarity index 60% rename from .github/workflows/main.yml rename to .github/workflows/build-only.yml index 1c40b6556..c23cb30c4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/build-only.yml @@ -3,29 +3,29 @@ name: CI on: push: branches: - - master - - develop - - feature/* - - feature-* - - issue/* - - issue-* + - master + - develop + - feature/* + - feature-* + - issue/* + - issue-* pull_request: branches: - - develop + - develop # macOS environments: https://github.com/actions/virtual-environments/tree/main/images/macos jobs: build: name: CI build - runs-on: macos-12 + runs-on: macos-13 steps: - - name: checkout + - name: Repository uses: actions/checkout@v2 - - name: setup - env: + - name: Setup Build Environment + env: NotificationEndpointDebug: ${{ secrets.NotificationEndpointDebug }} NotificationEndpointRelease: ${{ secrets.NotificationEndpointRelease }} run: exec ./.github/scripts/setup.sh - - name: build - run: exec ./.github/scripts/build.sh + - name: Build App + run: bundle exec fastlane ios build_only diff --git a/.github/workflows/deploy-appstore.yml b/.github/workflows/deploy-appstore.yml new file mode 100644 index 000000000..daba7cc7a --- /dev/null +++ b/.github/workflows/deploy-appstore.yml @@ -0,0 +1,40 @@ +name: Deploy App Store +on: + workflow_dispatch: + push: + branches: + - develop + - release* +jobs: + deploy_appstore: + name: Deploy App Store + runs-on: macos-13 + steps: + - name: Install SSH key + uses: webfactory/ssh-agent@v0.5.3 + with: + ssh-private-key: | + ${{ secrets.MATCH_SSH_PRIVATE_KEY }} + - name: Checkout Repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Build Environment + env: + NotificationEndpointDebug: ${{ secrets.NotificationEndpointDebug }} + NotificationEndpointRelease: ${{ secrets.NotificationEndpointRelease }} + run: exec ./.github/scripts/setup.sh + - name: Select required Xcode version + run: sudo xcode-select -switch /Applications/Xcode_15.0.0.app + - name: Deploy App Store + env: + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + ITC_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} + ITC_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} + ITC_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }} + run: bundle exec fastlane ios deploy_appstore + - name: Tag commit + uses: tvdias/github-tagger@v0.0.1 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + tag: "${{ env.GITHUB_TAG_NAME }}" diff --git a/.github/workflows/develop-build.yml b/.github/workflows/develop-build.yml deleted file mode 100644 index 7dd9ebaab..000000000 --- a/.github/workflows/develop-build.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Build for Develop TestFlight - -on: - push: - branches: - - develop - - release* - - ci-test - -jobs: - build: - name: Build - runs-on: macOS-12 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup - env: - NotificationEndpointDebug: ${{ secrets.NotificationEndpointDebug }} - NotificationEndpointRelease: ${{ secrets.NotificationEndpointRelease }} - run: exec ./.github/scripts/setup.sh - - - name: Install codemagic-cli-tools - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - run: | - pip3 install codemagic-cli-tools - - run: | - codemagic-cli-tools --version || true - - - name: Import Code-Signing Certificates - uses: Apple-Actions/import-codesign-certs@v1 # https://github.com/Apple-Actions/import-codesign-certs - with: - keychain: build-p12 - p12-file-base64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} - p12-password: ${{ secrets.P12_PASSWORD }} - - - name: Download Provisioning Profiles - uses: Apple-Actions/download-provisioning-profiles@v1 # https://github.com/Apple-Actions/download-provisioning-profiles - with: - bundle-id: org.joinmastodon.app - issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} - api-key-id: ${{ secrets.APPSTORE_KEY_ID }} - api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }} - - - name: Build - env: - ENV_APP_ID: ${{ secrets.APP_ID }} - ENV_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} - ENV_API_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} - ENV_API_PRIVATE_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }} - ENV_API_PRIVATE_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_KEY_BASE64 }} - run: exec ./.github/scripts/build-release.sh - - - name: Upload TestFlight Build - uses: Apple-Actions/upload-testflight-build@master - with: - app-path: .build/Artifacts/Mastodon.ipa/Mastodon.ipa - issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} - api-key-id: ${{ secrets.APPSTORE_KEY_ID }} - api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }} - - - name: Tag commit - uses: tvdias/github-tagger@v0.0.1 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - tag: "${{ env.GITHUB_TAG_NAME }}" - - - name: Clean up keychain and provisioning profile - if: ${{ always() }} - run: | - security delete-keychain build-p12.keychain-db diff --git a/.gitignore b/.gitignore index 742a2c40e..0e4d206bb 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,8 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output +fastlane/.env +fastlane/devices.txt # Code Injection # After new code Injection tools there's a generated folder /iOSInjectionProject diff --git a/Documentation/Deployment.md b/Documentation/Deployment.md new file mode 100644 index 000000000..5da41510d --- /dev/null +++ b/Documentation/Deployment.md @@ -0,0 +1,21 @@ +# Deployment + +## Your Device + +### As a Mastodon Collaborator WITH access to the Mastodon Apple Developer Program + +To ensure you're able to build the App for your Device please create a `fastlane/devices.txt` and add your device's UDID. +You may use `fastlane/devices.text.example` as a starting point. Please note that fastlane expects you to use tabs in this file. + +After adding your device please run `bundle exec fastlane ios register_devices` to add your device(s) to the Apple Develper Account. +Then run `bundle exec fastlane ios update_certificates` to re-generate the Codesigning Provisioning Profiles. + +You should now be able to run the App using Xcode on your Device. + +### As a Mastodon Contributor WITHOUT access to the Mastodon Apple Developer Program + +To run the App on your Device you'll need to take care of Codesigning yourself by adjusting the App's Codesigning Settings in Xcode to your needs. + +## App Store + +We're using [Fastlane](https://fastlane.tools) to deploy the App to App Store Connect. Please see the [Fastlane README](../fastlane/README.md) on the available commands. diff --git a/Gemfile b/Gemfile index 7ecafafc1..c3ea984b4 100644 --- a/Gemfile +++ b/Gemfile @@ -5,3 +5,7 @@ gem "cocoapods" gem "cocoapods-clean" gem "xcpretty" +# Fastlane +gem "fastlane" +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 98539bd5f..1b3792690 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,9 +4,14 @@ GEM CFPropertyList (3.0.6) rexml activesupport (7.0.8) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) @@ -17,12 +22,32 @@ GEM dotenv (~> 2.7) rainbow (~> 3.1.1) yaml (~> 0.2) + artifactory (3.0.15) atomos (0.1.3) + aws-eventstream (1.2.0) + aws-partitions (1.833.0) + aws-sdk-core (3.185.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.72.0) + aws-sdk-core (~> 3, >= 3.184.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.136.0) + aws-sdk-core (~> 3, >= 3.181.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.0) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.1.1) + bigdecimal (3.1.4) claide (1.1.0) - cocoapods (1.12.1) + cocoapods (1.13.0) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.12.1) + cocoapods-core (= 1.13.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.6.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -36,9 +61,9 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.21.0, < 2.0) + xcodeproj (>= 1.23.0, < 2.0) cocoapods-clean (0.0.1) - cocoapods-core (1.12.1) + cocoapods-core (1.13.0) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -57,35 +82,201 @@ GEM nap (>= 0.8, < 2.0) netrc (~> 0.11) cocoapods-try (1.2.0) + colored (1.2) colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) concurrent-ruby (1.2.2) + connection_pool (2.4.1) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) dotenv (2.8.1) + drb (2.1.1) + ruby2_keywords + emoji_regex (3.2.3) escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - ffi (1.15.5) + excon (0.104.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.1) + 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.2.7) + fastlane (2.216.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-versioning (0.5.2) + ffi (1.16.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.50.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.1) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) + domain_name (~> 0.5) httpclient (2.8.3) i18n (1.14.1) concurrent-ruby (~> 1.0) + jmespath (1.6.2) json (2.6.3) + jwt (2.7.1) + mini_magick (4.12.0) + mini_mime (1.1.5) minitest (5.20.0) molinillo (0.8.0) + multi_json (1.15.0) + multipart-post (2.3.0) + mutex_m (0.1.2) nanaimo (0.3.0) nap (1.1.0) + naturally (2.2.1) netrc (0.11.0) + optparse (0.1.1) + os (1.1.4) + plist (3.7.0) public_suffix (4.0.7) rainbow (3.1.1) + rake (13.0.6) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) rexml (3.2.6) rouge (2.0.7) ruby-macho (2.5.1) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.3) + signet (0.18.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) typhoeus (1.4.0) ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - xcodeproj (1.22.0) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) + unicode-display_width (2.5.0) + webrick (1.8.1) + word_wrap (1.0.0) + xcodeproj (1.23.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -94,6 +285,8 @@ GEM rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) yaml (0.2.1) PLATFORMS @@ -106,6 +299,8 @@ DEPENDENCIES arkana cocoapods cocoapods-clean + fastlane + fastlane-plugin-versioning xcpretty BUNDLED WITH diff --git a/Localization/StringsConvertor/input/Base.lproj/app.json b/Localization/StringsConvertor/input/Base.lproj/app.json index b57f0f4e4..978e865c2 100644 --- a/Localization/StringsConvertor/input/Base.lproj/app.json +++ b/Localization/StringsConvertor/input/Base.lproj/app.json @@ -715,6 +715,7 @@ "notifications": "Notifications", "support_mastodon": "Support Mastodon", "about_mastodon": "About Mastodon", + "server_details": "Server Details", "logout": "Logout %@" } @@ -725,6 +726,9 @@ "privacy_policy": "Privacy Policy", "clear_media_storage": "Clear Media Storage" }, + "about_instance": { + "message_admin": "Message Admin" + }, "general": { "title": "General", "appearance": { diff --git a/Localization/StringsConvertor/input/en.lproj/app.json b/Localization/StringsConvertor/input/en.lproj/app.json index b57f0f4e4..30630361f 100644 --- a/Localization/StringsConvertor/input/en.lproj/app.json +++ b/Localization/StringsConvertor/input/en.lproj/app.json @@ -715,6 +715,7 @@ "notifications": "Notifications", "support_mastodon": "Support Mastodon", "about_mastodon": "About Mastodon", + "server_details": "Server Details", "logout": "Logout %@" } diff --git a/Localization/app.json b/Localization/app.json index b57f0f4e4..90739402a 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -715,6 +715,7 @@ "notifications": "Notifications", "support_mastodon": "Support Mastodon", "about_mastodon": "About Mastodon", + "server_details": "Server Details", "logout": "Logout %@" } @@ -725,6 +726,16 @@ "privacy_policy": "Privacy Policy", "clear_media_storage": "Clear Media Storage" }, + + "server_details": { + "about": "About", + "rules": "Rules" + "about_instance": { + "title": "Adminstrator" + "message_admin": "Message Admin", + "legal_notice": "A legal notice" + } + }, "general": { "title": "General", "appearance": { diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 988e3c30e..307d3a540 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -20,7 +20,6 @@ 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */; }; 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; }; 164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; }; - 18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; }; 27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */; }; 2A1BF99529F7E68400FA1BA5 /* DataSourceFacade+UserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1BF99429F7E68400FA1BA5 /* DataSourceFacade+UserView.swift */; }; 2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; }; @@ -109,29 +108,31 @@ 5D0393962612D266007FE196 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0393952612D266007FE196 /* WebViewModel.swift */; }; 5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA732CB2629CEF500A92342 /* UIView+Remove.swift */; }; 5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; }; - 5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; }; - 5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; }; 6213AF5A28939C8400BCADB6 /* BookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213AF5928939C8400BCADB6 /* BookmarkViewModel.swift */; }; 6213AF5C28939C8A00BCADB6 /* BookmarkViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213AF5B28939C8A00BCADB6 /* BookmarkViewModel+State.swift */; }; 6213AF5E2893A8B200BCADB6 /* DataSourceFacade+Bookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213AF5D2893A8B200BCADB6 /* DataSourceFacade+Bookmark.swift */; }; 62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D02893707600B205C5 /* BookmarkViewController.swift */; }; 62FD27D32893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D22893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift */; }; 62FD27D52893708A00B205C5 /* BookmarkViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D42893708A00B205C5 /* BookmarkViewModel+Diffable.swift */; }; + 71458AF57697DB405CFEC37C /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C2A2448AEEDA65B4CD099FC /* Pods_Mastodon.framework */; }; + 7910197261F9D06EFCDCCDBC /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89FE8B85A00419CEF4678056 /* Pods_MastodonTests.framework */; }; 855149C8295F1C5F00943D96 /* UIInterfaceOrientationMask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855149C7295F1C5F00943D96 /* UIInterfaceOrientationMask.swift */; }; 855149CA29606D6400943D96 /* PortraitAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855149C929606D6400943D96 /* PortraitAlertController.swift */; }; 85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C01293BC0EB0011C817 /* ImageProvider.swift */; }; 85904C04293BC1940011C817 /* URLActivityItemWithMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */; }; 85BC11B32932414900E191CD /* AltTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BC11B22932414900E191CD /* AltTextViewController.swift */; }; - 87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; }; 9E44C7202967AD17004B2A72 /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 9E44C71F2967AD17004B2A72 /* MastodonSDKDynamic */; }; 9E44C7222967AD17004B2A72 /* MastodonSDKDynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 9E44C71F2967AD17004B2A72 /* MastodonSDKDynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; C24C97032922F30500BAE8CB /* RefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24C97022922F30500BAE8CB /* RefreshControl.swift */; }; D807C6C029DE197900A4E17C /* EducationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D807C6BF29DE197900A4E17C /* EducationViewController.swift */; }; D808B94C296ECFDC0031EB1E /* StatusEditHistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808B94B296ECFDC0031EB1E /* StatusEditHistoryViewModel.swift */; }; D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808B94D296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift */; }; + D80911082AC4BFDE00EB4D15 /* ServerDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */; }; D8099078294BC8A30050219F /* PrivacyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099077294BC8A30050219F /* PrivacyTableViewController.swift */; }; D809907A294BC9390050219F /* PrivacyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099079294BC9390050219F /* PrivacyTableViewCell.swift */; }; D809907C294D25510050219F /* PrivacyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D809907B294D25510050219F /* PrivacyViewModel.swift */; }; + D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439852AD415DE0071A88F /* AboutInstance.swift */; }; + D81439882AD450A40071A88F /* AboutInstanceTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */; }; D81A22752AB4643200905D71 /* SearchResultsOverviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */; }; D81A22782AB4782400905D71 /* SearchResultOverviewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */; }; D81A227B2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */; }; @@ -144,6 +145,8 @@ D8318A882A4468D300C0FB73 /* NotificationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */; }; D8318A8A2A4468DC00C0FB73 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A892A4468DC00C0FB73 /* AboutViewController.swift */; }; D8363B1629469CE200A74079 /* OnboardingNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8363B1529469CE200A74079 /* OnboardingNextView.swift */; }; + D852C23C2AC5D02C00309232 /* AboutInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */; }; + D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */; }; D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */; }; D87BFC8D291EB81200FEE264 /* MastodonLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */; }; D87BFC8F291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */; }; @@ -173,6 +176,10 @@ D8F917112A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F917102A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift */; }; D8F917122A4C6B67008A5370 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A832A4468A800C0FB73 /* GeneralSettingsViewController.swift */; }; D8F917142A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F917132A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift */; }; + D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */; }; + D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */; }; + D8FAAE412AD0475900DC1832 /* AboutInstanceTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */; }; + D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */; }; DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; settings = {ATTRIBUTES = (codegen, ); }; }; DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; }; DB023D26279FFB0A005AC798 /* ShareActivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */; }; @@ -488,6 +495,7 @@ DBFEEC99279BDCDE004F81DD /* ProfileAboutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC98279BDCDE004F81DD /* ProfileAboutViewModel.swift */; }; DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9A279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift */; }; DBFEEC9D279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEEC9C279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift */; }; + EF7771EAA493A869D65A105C /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD12474E169668871A9F6AB4 /* Pods_Mastodon_MastodonUITests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -713,7 +721,6 @@ 2D8434FA25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineNavigationBarTitleView.swift; sourceTree = ""; }; 2D84350425FF858100EECE90 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = ""; }; 2D939AB425EDD8A90076FA61 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; - 2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = ""; }; 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewController.swift; sourceTree = ""; }; 2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewModel.swift; sourceTree = ""; }; 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewCell.swift; sourceTree = ""; }; @@ -721,9 +728,7 @@ 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountSection.swift; sourceTree = ""; }; 2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = ""; }; 3B7FD8F28DDA8FBCE5562B78 /* Pods-NotificationService.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk - debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk - debug.xcconfig"; sourceTree = ""; }; - 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3E08A432F40BA7B9CAA9DB68 /* Pods-AppShared.release snapshot.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.release snapshot.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.release snapshot.xcconfig"; sourceTree = ""; }; - 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = ""; }; 46DAB0EBDDFB678347CD96FF /* Pods-MastodonTests.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.asdk - release.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.asdk - release.xcconfig"; sourceTree = ""; }; 5B24BBD7262DB14800A9381B /* ReportViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = ""; }; @@ -755,12 +760,13 @@ 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLActivityItemWithMetadata.swift; sourceTree = ""; }; 85BC11B22932414900E191CD /* AltTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AltTextViewController.swift; sourceTree = ""; }; 8850E70A1D5FF51432E43653 /* Pods-Mastodon-MastodonUITests.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; sourceTree = ""; }; + 89FE8B85A00419CEF4678056 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8C2A2448AEEDA65B4CD099FC /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8E79CCBE51FBC3F7FE8CF49F /* Pods-MastodonTests.release snapshot.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release snapshot.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release snapshot.xcconfig"; sourceTree = ""; }; 8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-NotificationService/Pods-Mastodon-NotificationService.debug.xcconfig"; sourceTree = ""; }; 9780A4C98FFC65B32B50D1C0 /* Pods-MastodonTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release.xcconfig"; sourceTree = ""; }; 9A0982D8F349244EB558CDFD /* Pods-AppShared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.debug.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.debug.xcconfig"; sourceTree = ""; }; 9CFF58FD900AC059428700E7 /* Pods-NotificationService.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk - release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk - release.xcconfig"; sourceTree = ""; }; - A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A67FD038ECDA0E411AF8DB4D /* Pods-Mastodon-MastodonUITests.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk.xcconfig"; sourceTree = ""; }; A9B1FB898DFD6063B044298C /* Pods-AppShared.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.asdk - debug.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.asdk - debug.xcconfig"; sourceTree = ""; }; B31D44635FCF6452F7E1B865 /* Pods-Mastodon-AppShared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.release.xcconfig"; sourceTree = ""; }; @@ -769,14 +775,16 @@ BD7598A87F4497045EDEF252 /* Pods-Mastodon.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk - release.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk - release.xcconfig"; sourceTree = ""; }; C24C97022922F30500BAE8CB /* RefreshControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshControl.swift; sourceTree = ""; }; C3789232A52F43529CA67E95 /* Pods-MastodonIntent.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonIntent.asdk - debug.xcconfig"; path = "Target Support Files/Pods-MastodonIntent/Pods-MastodonIntent.asdk - debug.xcconfig"; sourceTree = ""; }; - CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D7D7CF93E262178800077512 /* Pods-Mastodon-AppShared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.debug.xcconfig"; sourceTree = ""; }; D807C6BF29DE197900A4E17C /* EducationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EducationViewController.swift; sourceTree = ""; }; D808B94B296ECFDC0031EB1E /* StatusEditHistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusEditHistoryViewModel.swift; sourceTree = ""; }; D808B94D296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusEditHistoryTableViewCell.swift; sourceTree = ""; }; + D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailsViewController.swift; sourceTree = ""; }; D8099077294BC8A30050219F /* PrivacyTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewController.swift; sourceTree = ""; }; D8099079294BC9390050219F /* PrivacyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewCell.swift; sourceTree = ""; }; D809907B294D25510050219F /* PrivacyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyViewModel.swift; sourceTree = ""; }; + D81439852AD415DE0071A88F /* AboutInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstance.swift; sourceTree = ""; }; + D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableViewDataSource.swift; sourceTree = ""; }; D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsOverviewTableViewController.swift; sourceTree = ""; }; D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultOverviewSection.swift; sourceTree = ""; }; D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultDefaultSectionTableViewCell.swift; sourceTree = ""; }; @@ -786,7 +794,6 @@ D82463532A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/WidgetExtension.strings; sourceTree = ""; }; D82463542A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/InfoPlist.strings; sourceTree = ""; }; D82463552A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = be.lproj/Intents.stringsdict; sourceTree = ""; }; - D82BD7512ABC42D6009A374A /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; D82BD7542ABC73AF009A374A /* NotificationPolicyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPolicyTableViewCell.swift; sourceTree = ""; }; D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = ""; }; D8318A832A4468A800C0FB73 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = ""; }; @@ -794,6 +801,8 @@ D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsViewController.swift; sourceTree = ""; }; D8318A892A4468DC00C0FB73 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; D8363B1529469CE200A74079 /* OnboardingNextView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = OnboardingNextView.swift; sourceTree = ""; tabWidth = 4; }; + D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceViewController.swift; sourceTree = ""; }; + D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceRulesViewController.swift; sourceTree = ""; }; D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginView.swift; sourceTree = ""; }; D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewModel.swift; sourceTree = ""; }; D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginServerTableViewCell.swift; sourceTree = ""; }; @@ -833,6 +842,10 @@ D8F9170E2A4B47EF008A5370 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; D8F917102A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingToggleTableViewCell.swift; sourceTree = ""; }; D8F917132A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsDiffableTableViewDataSource.swift; sourceTree = ""; }; + D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminTableViewCell.swift; sourceTree = ""; }; + D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAdminTableViewCell.swift; sourceTree = ""; }; + D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableViewHeader.swift; sourceTree = ""; }; + D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableFooterView.swift; sourceTree = ""; }; DB0009A826AEE5DC009B9D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; DB0009AD26AEE5E4009B9D2D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Intents.strings; sourceTree = ""; }; DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareActivityProvider.swift; sourceTree = ""; }; @@ -896,7 +909,6 @@ DB1FD44325F26CCC004CFCFC /* PickServerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerSection.swift; sourceTree = ""; }; DB1FD44F25F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonPickServerViewModel+Diffable.swift"; sourceTree = ""; }; DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerItem.swift; sourceTree = ""; }; - DB1FD45F25F278AF004CFCFC /* CategoryPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerSection.swift; sourceTree = ""; }; DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = ""; }; DB336F3E278E668C0031E64B /* StatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusTableViewCell+ViewModel.swift"; sourceTree = ""; }; @@ -1237,6 +1249,7 @@ DBFEEC9A279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileAboutViewModel+Diffable.swift"; sourceTree = ""; }; DBFEEC9C279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldEditCollectionViewCell.swift; sourceTree = ""; }; DBFEF06726A58D07006D7ED1 /* ShareActionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareActionExtension.entitlements; sourceTree = ""; }; + DD12474E169668871A9F6AB4 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DDB1B139FA8EA26F510D58B6 /* Pods-AppShared.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.asdk - release.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.asdk - release.xcconfig"; sourceTree = ""; }; DF65937EC1FF64462BC002EE /* Pods-MastodonTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.profile.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.profile.xcconfig"; sourceTree = ""; }; E5C7236E58D14A0322FE00F2 /* Pods-Mastodon-MastodonUITests.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk - debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk - debug.xcconfig"; sourceTree = ""; }; @@ -1246,7 +1259,6 @@ ECA373ABA86BE3C2D7ED878E /* Pods-AppShared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.release.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.release.xcconfig"; sourceTree = ""; }; EE13214BC0246BE5210CCC10 /* Pods-AppShared.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppShared.asdk.xcconfig"; path = "Target Support Files/Pods-AppShared/Pods-AppShared.asdk.xcconfig"; sourceTree = ""; }; F31E7502A7E3945B98C6CBAF /* Pods-NotificationService.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk.xcconfig"; sourceTree = ""; }; - F4A2A2D7000E477CA459ADA9 /* Pods_AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F920AD4EC23B0D00F5CCA58E /* Pods-MastodonIntent.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonIntent.asdk - release.xcconfig"; path = "Target Support Files/Pods-MastodonIntent/Pods-MastodonIntent.asdk - release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1277,7 +1289,7 @@ files = ( 357FEEAF29523D470021C9DC /* MastodonSDKDynamic in Frameworks */, DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */, - 87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */, + 71458AF57697DB405CFEC37C /* Pods_Mastodon.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1286,8 +1298,7 @@ buildActionMask = 2147483647; files = ( 9E44C7202967AD17004B2A72 /* MastodonSDKDynamic in Frameworks */, - 5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */, - 5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */, + 7910197261F9D06EFCDCCDBC /* Pods_MastodonTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1295,7 +1306,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */, + EF7771EAA493A869D65A105C /* Pods_Mastodon_MastodonUITests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1720,15 +1731,14 @@ isa = PBXGroup; children = ( DBF96325262EC0A6001D8D25 /* AuthenticationServices.framework */, - A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */, - 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */, - 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */, - F4A2A2D7000E477CA459ADA9 /* Pods_AppShared.framework */, DB8FAB9E26AEC3A2008E5AF4 /* Intents.framework */, DB8FABA926AEC3A2008E5AF4 /* IntentsUI.framework */, 2A6451022964223800CD8B8A /* UniformTypeIdentifiers.framework */, 2A728121297EA9D7004138C5 /* WidgetKit.framework */, 2A728123297EA9D7004138C5 /* SwiftUI.framework */, + 8C2A2448AEEDA65B4CD099FC /* Pods_Mastodon.framework */, + DD12474E169668871A9F6AB4 /* Pods_Mastodon_MastodonUITests.framework */, + 89FE8B85A00419CEF4678056 /* Pods_MastodonTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -1754,6 +1764,7 @@ D8F916FF2A4AD898008A5370 /* Settings Overview */, D8F917042A4B0657008A5370 /* General Settings */, D81D12432A4E181C005009D4 /* Notification Settings */, + D80911062AC4BFD100EB4D15 /* Server Details */, D8F917092A4B2AFF008A5370 /* About Mastodon */, D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */, ); @@ -1781,6 +1792,17 @@ path = Bookmark; sourceTree = ""; }; + D80911062AC4BFD100EB4D15 /* Server Details */ = { + isa = PBXGroup; + children = ( + D8FAAE3B2AD042CD00DC1832 /* Table View Components */, + D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */, + D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */, + D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */, + ); + path = "Server Details"; + sourceTree = ""; + }; D8099076294BC2BA0050219F /* Privacy */ = { isa = PBXGroup; children = ( @@ -1927,6 +1949,19 @@ path = "About Mastodon"; sourceTree = ""; }; + D8FAAE3B2AD042CD00DC1832 /* Table View Components */ = { + isa = PBXGroup; + children = ( + D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */, + D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */, + D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */, + D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */, + D81439852AD415DE0071A88F /* AboutInstance.swift */, + D81439872AD450A40071A88F /* AboutInstanceTableViewDataSource.swift */, + ); + path = "Table View Components"; + sourceTree = ""; + }; DB01409B25C40BB600F9F3CF /* Onboarding */ = { isa = PBXGroup; children = ( @@ -2108,7 +2143,6 @@ DB427DD325BAA00100D1B89D /* Products */, 1EBA4F56E920856A3FC84ACB /* Pods */, 3FE14AD363ED19AE7FF210A6 /* Frameworks */, - DB98335F25C93B0400AD9700 /* Recovered References */, D8A6FE6029325F5900666A47 /* Localization */, ); indentWidth = 4; @@ -2635,17 +2669,6 @@ path = Thread; sourceTree = ""; }; - DB98335F25C93B0400AD9700 /* Recovered References */ = { - isa = PBXGroup; - children = ( - CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */, - 2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */, - DB1FD45F25F278AF004CFCFC /* CategoryPickerSection.swift */, - D82BD7512ABC42D6009A374A /* Coordinator.swift */, - ); - name = "Recovered References"; - sourceTree = ""; - }; DB98EB4A27B0F0F50082E365 /* Cell */ = { isa = PBXGroup; children = ( @@ -3266,7 +3289,7 @@ }; }; buildConfigurationList = DB427DCD25BAA00100D1B89D /* Build configuration list for PBXProject "Mastodon" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 15.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -3612,6 +3635,7 @@ DB023D26279FFB0A005AC798 /* ShareActivityProvider.swift in Sources */, 5D0393962612D266007FE196 /* WebViewModel.swift in Sources */, 5B24BBDA262DB14800A9381B /* ReportViewModel.swift in Sources */, + D80911082AC4BFDE00EB4D15 /* ServerDetailsViewController.swift in Sources */, 2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */, DB6180EF26391CA50018D199 /* MediaPreviewImageViewController.swift in Sources */, DB1E347825F519300079D7DF /* PickServerItem.swift in Sources */, @@ -3639,11 +3663,14 @@ DB697DD6278F4C29004EF2F7 /* DataSourceProvider.swift in Sources */, DB0FCB8E2796C0B7006C02E2 /* TrendCollectionViewCell.swift in Sources */, 0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */, + D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */, DBDFF1902805543100557A48 /* DiscoveryPostsViewController.swift in Sources */, DB697DD9278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, + D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */, D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */, 2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */, + D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */, DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */, DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */, 62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */, @@ -3662,6 +3689,7 @@ DBDFF19C28055BD600557A48 /* DiscoveryViewModel.swift in Sources */, DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */, DB3E6FF32806D97400B035AE /* DiscoveryNewsViewModel+State.swift in Sources */, + D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */, DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */, DB0618032785A7100030EE79 /* RegisterSection.swift in Sources */, DB63F76B279A5ED300455B82 /* NotificationTimelineViewModel+LoadOldestState.swift in Sources */, @@ -3690,6 +3718,7 @@ DBFEEC9D279C12C1004F81DD /* ProfileFieldEditCollectionViewCell.swift in Sources */, DB3E6FEC2806D7F100B035AE /* DiscoveryNewsViewController.swift in Sources */, DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */, + D8FAAE412AD0475900DC1832 /* AboutInstanceTableViewHeader.swift in Sources */, 2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */, 2DAC9E3E262FC2400062E1A6 /* SuggestionAccountViewModel.swift in Sources */, DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */, @@ -3853,6 +3882,7 @@ DB68A04A25E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift in Sources */, 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */, 6213AF5C28939C8A00BCADB6 /* BookmarkViewModel+State.swift in Sources */, + D81439882AD450A40071A88F /* AboutInstanceTableViewDataSource.swift in Sources */, D807C6C029DE197900A4E17C /* EducationViewController.swift in Sources */, 2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */, DBEFCD7B282A162400C0ABEA /* ReportReasonView.swift in Sources */, @@ -3920,6 +3950,7 @@ D8B5E4F42A4ED0240008970C /* NotificationSettingsViewModel.swift in Sources */, DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */, DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */, + D852C23C2AC5D02C00309232 /* AboutInstanceViewController.swift in Sources */, D8F917142A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift in Sources */, DB6180FA26391F2E0018D199 /* MediaPreviewViewModel.swift in Sources */, 2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */, @@ -3935,6 +3966,7 @@ DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */, DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */, DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */, + D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */, DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */, D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */, DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */, @@ -4268,14 +4300,12 @@ CODE_SIGN_ENTITLEMENTS = OpenInActionExtension/OpenInActionExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OpenInActionExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Open using Mastodon"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4284,6 +4314,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.OpenInActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -4297,15 +4328,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = Icon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = OpenInActionExtension/OpenInActionExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OpenInActionExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Open using Mastodon"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4327,15 +4354,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = Icon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = OpenInActionExtension/OpenInActionExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; DEVELOPMENT_TEAM = 5Z4GVSS33P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OpenInActionExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Open using Mastodon"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4344,6 +4369,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.OpenInActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore org.joinmastodon.app.OpenInActionExtension"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -4357,15 +4383,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = Icon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = OpenInActionExtension/OpenInActionExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OpenInActionExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Open using Mastodon"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4390,14 +4412,12 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WidgetExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4406,6 +4426,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.WidgetExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -4420,15 +4441,11 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WidgetExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4451,15 +4468,13 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; DEVELOPMENT_TEAM = 5Z4GVSS33P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WidgetExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4468,6 +4483,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.WidgetExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore org.joinmastodon.app.WidgetExtension"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -4482,15 +4498,11 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5Z4GVSS33P; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WidgetExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -4560,7 +4572,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INTENTS_CODEGEN_LANGUAGE = Swift; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -4604,9 +4616,12 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Apple Distribution"; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 5Z4GVSS33P; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -4618,7 +4633,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INTENTS_CODEGEN_LANGUAGE = Swift; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -4668,10 +4683,9 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; - DEVELOPMENT_TEAM = 5Z4GVSS33P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5Z4GVSS33P; EXCLUDED_SOURCE_FILE_NAMES = "Mastodon/Resources/Preview\\ Assets.xcassets"; INFOPLIST_FILE = Mastodon/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; @@ -4683,6 +4697,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore org.joinmastodon.app"; SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4696,6 +4711,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4720,6 +4736,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4743,6 +4760,7 @@ baseConfigurationReference = 459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4766,6 +4784,7 @@ baseConfigurationReference = BB482D32A7B9825BF5327C4F /* Pods-Mastodon-MastodonUITests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4837,7 +4856,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INTENTS_CODEGEN_LANGUAGE = Swift; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -4856,8 +4875,6 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4884,6 +4901,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4907,6 +4925,7 @@ baseConfigurationReference = 728DE51ADA27C395C6E1BAB5 /* Pods-Mastodon-MastodonUITests.profile.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4930,7 +4949,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4951,7 +4969,6 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4973,7 +4990,6 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -4995,6 +5011,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; @@ -5005,6 +5022,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; @@ -5017,8 +5035,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; DEVELOPMENT_TEAM = 5Z4GVSS33P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -5027,6 +5046,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent; PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore org.joinmastodon.app.MastodonIntent"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; @@ -5039,6 +5059,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; @@ -5049,6 +5070,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; @@ -5061,8 +5083,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; DEVELOPMENT_TEAM = 5Z4GVSS33P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -5071,6 +5094,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore org.joinmastodon.app.ShareActionExtension"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)"; SWIFT_VERSION = 5.0; @@ -5127,7 +5151,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INTENTS_CODEGEN_LANGUAGE = Swift; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -5147,8 +5171,6 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5174,6 +5196,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5197,6 +5220,7 @@ baseConfigurationReference = 0827D1674B2523503E8605F6 /* Pods-Mastodon-MastodonUITests.release snapshot.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 5889; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5220,7 +5244,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5241,7 +5264,6 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5263,7 +5285,6 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; - CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5286,6 +5307,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; @@ -5296,6 +5318,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -5308,8 +5331,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; DEVELOPMENT_TEAM = 5Z4GVSS33P; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -5318,6 +5342,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore org.joinmastodon.app.NotificationService"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index c6e38eff3..6e737d55c 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -46,6 +46,15 @@ "version": "3.1.3" } }, + { + "package": "FXPageControl", + "repositoryURL": "https://github.com/nicklockwood/FXPageControl.git", + "state": { + "branch": null, + "revision": "a94633402ba98c52f86c2a70e61ff086dec9de78", + "version": "1.6.0" + } + }, { "package": "KeychainAccess", "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git", @@ -91,15 +100,6 @@ "version": "10.11.2" } }, - { - "package": "NukeFLAnimatedImagePlugin", - "repositoryURL": "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", - "state": { - "branch": null, - "revision": "b59c346a7d536336db3b0f12c72c6e53ee709e16", - "version": "8.0.0" - } - }, { "package": "Pageboy", "repositoryURL": "https://github.com/uias/Pageboy", @@ -216,6 +216,15 @@ "revision": "20f513ded04a040cdf5467f0891849b1763ede3b", "version": "1.4.1" } + }, + { + "package": "XLPagerTabStrip", + "repositoryURL": "https://github.com/xmartlabs/XLPagerTabStrip.git", + "state": { + "branch": null, + "revision": "211ed62aa376722cf93c429802a8b6ff66a8bd52", + "version": "9.1.0" + } } ] }, diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 331a0b0a7..223ac4588 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -541,7 +541,9 @@ private extension SceneCoordinator { accountName: accountName, setting: setting, appContext: appContext, - authContext: authContext) + authContext: authContext, + sceneCoordinator: self + ) settingsCoordinator.delegate = self settingsCoordinator.start() @@ -643,7 +645,5 @@ extension SceneCoordinator: SettingsCoordinatorDelegate { authenticationController.authenticationSession?.start() self.mastodonAuthenticationController = authenticationController - - } } diff --git a/Mastodon/Scene/MediaPreview/AltTextViewController.swift b/Mastodon/Scene/MediaPreview/AltTextViewController.swift index 15f79b805..a40ad47d8 100644 --- a/Mastodon/Scene/MediaPreview/AltTextViewController.swift +++ b/Mastodon/Scene/MediaPreview/AltTextViewController.swift @@ -9,15 +9,7 @@ import SwiftUI class AltTextViewController: UIViewController { let textView = { - let textView: UITextView - - if #available(iOS 16, *) { - // TODO: update code below to use TextKit 2 when dropping iOS 15 support - textView = UITextView(usingTextLayoutManager: false) - } else { - textView = UITextView() - } - + let textView = UITextView(usingTextLayoutManager: false) textView.textContainer.maximumNumberOfLines = 0 textView.textContainer.lineBreakMode = .byWordWrapping textView.font = .preferredFont(forTextStyle: .callout) diff --git a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift index 10840c35f..f750e5a26 100644 --- a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift +++ b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift @@ -31,17 +31,7 @@ final class MediaPreviewImageView: UIScrollView { private var containerFrame: CGRect? - private var _interaction: UIInteraction? = { - if #available(iOS 16.0, *) { - return ImageAnalysisInteraction() - } else { - return nil - } - }() - @available(iOS 16.0, *) - var liveTextInteraction: ImageAnalysisInteraction { - _interaction as! ImageAnalysisInteraction - } + let liveTextInteraction = ImageAnalysisInteraction() override init(frame: CGRect) { super.init(frame: frame) @@ -72,9 +62,7 @@ extension MediaPreviewImageView { doubleTapGestureRecognizer.delegate = self imageView.addGestureRecognizer(doubleTapGestureRecognizer) - if #available(iOS 16.0, *) { - imageView.addInteraction(liveTextInteraction) - } + imageView.addInteraction(liveTextInteraction) delegate = self } @@ -119,8 +107,7 @@ extension MediaPreviewImageView: UIGestureRecognizerDelegate { // but only if the Live Text button is toggled off if let gr = otherGestureRecognizer as? UITapGestureRecognizer, gr.numberOfTapsRequired == 2, - #available(iOS 16, *), - !liveTextInteraction.selectableItemsHighlighted { + liveTextInteraction.selectableItemsHighlighted == false { return true } return false @@ -151,18 +138,16 @@ extension MediaPreviewImageView { centerScrollViewContents() - if #available(iOS 16.0, *) { - Task.detached(priority: .userInitiated) { - do { - let analysis = try await ImageAnalyzer.shared.analyze(image, configuration: ImageAnalyzer.Configuration([.text, .machineReadableCode])) - await MainActor.run { - self.liveTextInteraction.analysis = analysis - self.liveTextInteraction.preferredInteractionTypes = .automatic - } - } catch { - await MainActor.run { - self.liveTextInteraction.preferredInteractionTypes = [] - } + Task.detached(priority: .userInitiated) { + do { + let analysis = try await ImageAnalyzer.shared.analyze(image, configuration: ImageAnalyzer.Configuration([.text, .machineReadableCode])) + await MainActor.run { + self.liveTextInteraction.analysis = analysis + self.liveTextInteraction.preferredInteractionTypes = .automatic + } + } catch { + await MainActor.run { + self.liveTextInteraction.preferredInteractionTypes = [] } } } diff --git a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift index 2fd2231a1..6a50e1fc6 100644 --- a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift +++ b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift @@ -40,9 +40,7 @@ extension MediaPreviewImageViewController { override func viewDidLoad() { super.viewDidLoad() - if #available(iOS 16.0, *) { - previewImageView.liveTextInteraction.delegate = self - } + previewImageView.liveTextInteraction.delegate = self previewImageView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(previewImageView) NSLayoutConstraint.activate([ @@ -90,16 +88,13 @@ extension MediaPreviewImageViewController { extension MediaPreviewImageViewController: MediaPreviewPage { func setShowingChrome(_ showingChrome: Bool) { - if #available(iOS 16.0, *) { - UIView.animate(withDuration: 0.3) { - self.previewImageView.liveTextInteraction.setSupplementaryInterfaceHidden(!showingChrome, animated: true) - } + UIView.animate(withDuration: 0.3) { + self.previewImageView.liveTextInteraction.setSupplementaryInterfaceHidden(!showingChrome, animated: true) } } } // MARK: - ImageAnalysisInteractionDelegate -@available(iOS 16.0, *) extension MediaPreviewImageViewController: ImageAnalysisInteractionDelegate { func presentingViewController(for interaction: ImageAnalysisInteraction) -> UIViewController? { self @@ -109,18 +104,14 @@ extension MediaPreviewImageViewController: ImageAnalysisInteractionDelegate { // MARK: - UIGestureRecognizerDelegate extension MediaPreviewImageViewController: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - if #available(iOS 16.0, *) { - let location = touch.location(in: previewImageView.imageView) - // for tap gestures, only items that can be tapped are relevant - if gestureRecognizer is UITapGestureRecognizer { - return !previewImageView.liveTextInteraction.hasSupplementaryInterface(at: location) - && !previewImageView.liveTextInteraction.hasDataDetector(at: location) - } else { - // for long press, block out everything - return !previewImageView.liveTextInteraction.hasInteractiveItem(at: location) - } + let location = touch.location(in: previewImageView.imageView) + // for tap gestures, only items that can be tapped are relevant + if gestureRecognizer is UITapGestureRecognizer { + return !previewImageView.liveTextInteraction.hasSupplementaryInterface(at: location) + && !previewImageView.liveTextInteraction.hasDataDetector(at: location) } else { - return true + // for long press, block out everything + return !previewImageView.liveTextInteraction.hasInteractiveItem(at: location) } } } @@ -129,10 +120,8 @@ extension MediaPreviewImageViewController: UIGestureRecognizerDelegate { extension MediaPreviewImageViewController: UIContextMenuInteractionDelegate { func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { - if #available(iOS 16.0, *) { - if previewImageView.liveTextInteraction.hasInteractiveItem(at: previewImageView.imageView.convert(location, from: previewImageView)) { - return nil - } + if previewImageView.liveTextInteraction.hasInteractiveItem(at: previewImageView.imageView.convert(location, from: previewImageView)) { + return nil } diff --git a/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift b/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift index 7eeec22e5..6bf513878 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/Cell/ServerRulesTableViewCell.swift @@ -10,7 +10,7 @@ import MastodonAsset import MastodonLocalization final class ServerRulesTableViewCell: UITableViewCell { - + static let reuseIdentifier = "ServerRulesTableViewCell" static let margin: CGFloat = 23 let indexImageView: UIImageView = { diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift index 1f8d55c76..bbae5c324 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift @@ -15,10 +15,7 @@ extension MastodonServerRulesViewModel { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.rules]) - let ruleItems: [ServerRuleItem] = rules.enumerated().map { i, rule in - let ruleContext = ServerRuleItem.RuleContext(index: i, rule: rule) - return ServerRuleItem.rule(ruleContext) - } + let ruleItems: [ServerRuleItem] = rules.enumerated().map { index, rule in return ServerRuleItem.rule(index: index, rule: rule) } snapshot.appendItems(ruleItems, toSection: .rules) diffableDataSource?.apply(snapshot, animatingDifferences: false) } diff --git a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift index f82b1e5e4..dd6695386 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleItem.swift @@ -9,12 +9,5 @@ import Foundation import MastodonSDK enum ServerRuleItem: Hashable { - case rule(RuleContext) -} - -extension ServerRuleItem { - struct RuleContext: Hashable { - let index: Int - let rule: Mastodon.Entity.Instance.Rule - } + case rule(index: Int, rule: Mastodon.Entity.Instance.Rule) } diff --git a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift index ca4cd9c13..0f121baf5 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/ServerRuleSection.swift @@ -19,11 +19,11 @@ extension ServerRuleSection { ) -> UITableViewDiffableDataSource { return UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in switch item { - case .rule(let ruleContext): + case .rule(let index, let rule): let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ServerRulesTableViewCell.self), for: indexPath) as! ServerRulesTableViewCell - cell.indexImageView.image = UIImage(systemName: "\(ruleContext.index + 1).circle") ?? UIImage(systemName: "questionmark.circle") + cell.indexImageView.image = UIImage(systemName: "\(index + 1).circle") ?? UIImage(systemName: "questionmark.circle") cell.indexImageView.tintColor = Asset.Colors.Brand.lightBlurple.color - cell.ruleLabel.text = ruleContext.rule.text + cell.ruleLabel.text = rule.text return cell } } diff --git a/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift b/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift index df0719ff1..099799c6e 100644 --- a/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift +++ b/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift @@ -28,13 +28,8 @@ final class ProfileFieldCollectionViewCell: UICollectionViewCell { let checkmark = UIImageView(image: Asset.Editing.checkmark.image.withRenderingMode(.alwaysTemplate)) var checkmarkPopoverString: String? = nil; let tapGesture = UITapGestureRecognizer(); - private var _editMenuInteraction: Any? = nil - @available(iOS 16, *) - fileprivate var editMenuInteraction: UIEditMenuInteraction { - _editMenuInteraction = _editMenuInteraction ?? UIEditMenuInteraction(delegate: self) - return _editMenuInteraction as! UIEditMenuInteraction - } - + var editMenuInteraction: UIEditMenuInteraction! + override func prepareForReuse() { super.prepareForReuse() @@ -56,6 +51,9 @@ final class ProfileFieldCollectionViewCell: UICollectionViewCell { extension ProfileFieldCollectionViewCell { private func _init() { + + editMenuInteraction = UIEditMenuInteraction(delegate: self) + // Setup colors checkmark.tintColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color; @@ -63,10 +61,8 @@ extension ProfileFieldCollectionViewCell { tapGesture.addTarget(self, action: #selector(ProfileFieldCollectionViewCell.didTapCheckmark(_:))) checkmark.addGestureRecognizer(tapGesture) checkmark.isUserInteractionEnabled = true - if #available(iOS 16, *) { - checkmark.addInteraction(editMenuInteraction) - } - + checkmark.addInteraction(editMenuInteraction) + // Setup Accessibility checkmark.isAccessibilityElement = true checkmark.accessibilityTraits = .none @@ -111,22 +107,7 @@ extension ProfileFieldCollectionViewCell { } @objc public func didTapCheckmark(_ recognizer: UITapGestureRecognizer) { - if #available(iOS 16, *) { - editMenuInteraction.presentEditMenu(with: UIEditMenuConfiguration(identifier: nil, sourcePoint: recognizer.location(in: checkmark))) - } else { - guard let editMenuLabel = checkmarkPopoverString else { return } - - self.isUserInteractionEnabled = true - self.becomeFirstResponder() - - UIMenuController.shared.menuItems = [ - UIMenuItem( - title: editMenuLabel, - action: #selector(dismissVerifiedMenu) - ) - ] - UIMenuController.shared.showMenu(from: checkmark, rect: checkmark.bounds) - } + editMenuInteraction?.presentEditMenu(with: UIEditMenuConfiguration(identifier: nil, sourcePoint: recognizer.location(in: checkmark))) } private var valueMetas: [(title: String, Meta)] { @@ -190,7 +171,6 @@ extension ProfileFieldCollectionViewCell: MetaLabelDelegate { } // MARK: UIEditMenuInteractionDelegate -@available(iOS 16.0, *) extension ProfileFieldCollectionViewCell: UIEditMenuInteractionDelegate { func editMenuInteraction(_ interaction: UIEditMenuInteraction, menuFor configuration: UIEditMenuConfiguration, suggestedActions: [UIMenuElement]) -> UIMenu? { guard let editMenuLabel = checkmarkPopoverString else { return UIMenu(children: []) } diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index 0ec7e9bb7..10ed4c441 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -62,11 +62,7 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { // disable edge swipe gesture presentsWithGesture = false - if #available(iOS 14.5, *) { - displayModeButtonVisibility = .never - } else { - // Fallback on earlier versions - } + displayModeButtonVisibility = .never setViewController(searchViewController, for: .primary) setViewController(contentSplitViewController, for: .secondary) diff --git a/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift b/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift index fcb952f30..3c6ca64bc 100644 --- a/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift +++ b/Mastodon/Scene/Settings/About Mastodon/AboutSettings.swift @@ -23,7 +23,7 @@ enum AboutSettingsEntry: Hashable { case .privacyPolicy: return L10n.Scene.Settings.AboutMastodon.privacyPolicy case .clearMediaCache(_): - return L10n.Scene.Settings.AboutMastodon.cleaerMediaStorage + return L10n.Scene.Settings.AboutMastodon.clearMediaStorage } } diff --git a/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift b/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift new file mode 100644 index 000000000..59c1d5b01 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift @@ -0,0 +1,155 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonSDK + +protocol AboutInstanceViewControllerDelegate: AnyObject { + func showAdminAccount(_ viewController: AboutInstanceViewController, account: Mastodon.Entity.Account) + func sendEmailToAdmin(_ viewController: AboutInstanceViewController, emailAddress: String) +} + +class AboutInstanceViewController: UIViewController { + + weak var delegate: AboutInstanceViewControllerDelegate? + var dataSource: AboutInstanceTableViewDataSource? + + let tableView: UITableView + let headerView: AboutInstanceTableHeaderView + let footerView: AboutInstanceTableFooterView + + var instance: Mastodon.Entity.V2.Instance? + + init() { + tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.register(ContactAdminTableViewCell.self, forCellReuseIdentifier: ContactAdminTableViewCell.reuseIdentifier) + tableView.register(AdminTableViewCell.self, forCellReuseIdentifier: AdminTableViewCell.reuseIdentifier) + + headerView = AboutInstanceTableHeaderView() + footerView = AboutInstanceTableFooterView() + + super.init(nibName: nil, bundle: nil) + + let dataSource = AboutInstanceTableViewDataSource(tableView: tableView) { tableView, indexPath, itemIdentifier in + switch itemIdentifier { + + case .adminAccount(let account): + guard let cell = tableView.dequeueReusableCell(withIdentifier: AdminTableViewCell.reuseIdentifier, for: indexPath) as? AdminTableViewCell else { fatalError("WTF?! Wrong cell.") } + + cell.condensedUserView.configure(with: account, showFollowers: false) + + return cell + + case .contactAdmin: + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactAdminTableViewCell.reuseIdentifier, for: indexPath) as? ContactAdminTableViewCell else { fatalError("WTF?! Wrong cell.") } + + cell.configure() + + return cell + } + } + + tableView.delegate = self + tableView.dataSource = dataSource + + self.dataSource = dataSource + + view.addSubview(tableView) + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: tableView.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.tableHeaderView = headerView + tableView.tableFooterView = footerView + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + + if let tableHeaderView = tableView.tableHeaderView { + let size = tableHeaderView.systemLayoutSizeFitting(.init(width: self.tableView.frame.width, height: 10_000)) + tableHeaderView.frame.size = size + tableView.tableHeaderView = tableHeaderView + } + + if let tableFooterView = tableView.tableFooterView { + let size = tableFooterView.systemLayoutSizeFitting(.init(width: self.tableView.frame.width, height: 10_000)) + tableFooterView.frame.size = size + tableView.tableFooterView = tableFooterView + } + + super.viewWillLayoutSubviews() + } + + func update(with instance: Mastodon.Entity.V2.Instance) { + + self.instance = instance + var snapshot = NSDiffableDataSourceSnapshot() + + snapshot.appendSections([.main]) + if let account = instance.contact?.account { + snapshot.appendItems([.adminAccount(account)], toSection: .main) + } + + if let email = instance.contact?.email { + snapshot.appendItems([.contactAdmin(email)], toSection: .main) + } + + dataSource?.apply(snapshot, animatingDifferences: false) + + guard let thumbnailUrlString = instance.thumbnail?.url, let thumbnailUrl = URL(string: thumbnailUrlString) else { return } + + DispatchQueue.main.async { + self.headerView.updateImage(with: thumbnailUrl) { [weak self] in + DispatchQueue.main.async { + guard let self else { return } + + self.view.setNeedsLayout() + self.view.layoutIfNeeded() + } + } + } + } + + func updateFooter(with extendedDescription: Mastodon.Entity.ExtendedDescription) { + DispatchQueue.main.async { + self.footerView.update(with: extendedDescription) + + self.view.setNeedsLayout() + self.view.layoutIfNeeded() + } + } +} + +extension AboutInstanceViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let snapshot = dataSource?.snapshot() else { + return tableView.deselectRow(at: indexPath, animated: true) + } + + switch snapshot.itemIdentifiers(inSection: .main)[indexPath.row] { + case .adminAccount(let account): + delegate?.showAdminAccount(self, account: account) + case .contactAdmin(let email): + delegate?.sendEmailToAdmin(self, emailAddress: email) + } + + tableView.deselectRow(at: indexPath, animated: true) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/InstanceRulesViewController.swift b/Mastodon/Scene/Settings/Server Details/InstanceRulesViewController.swift new file mode 100644 index 000000000..b4d934214 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/InstanceRulesViewController.swift @@ -0,0 +1,58 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonSDK + +protocol InstanceRulesViewControllerDelegate: AnyObject { + +} + +class InstanceRulesViewController: UIViewController { + + weak var delegate: InstanceRulesViewControllerDelegate? + let tableView: UITableView + var dataSource: UITableViewDiffableDataSource? + + var sections: [ServerRuleSection] = [] + + init() { + tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.register(ServerRulesTableViewCell.self, forCellReuseIdentifier: ServerRulesTableViewCell.reuseIdentifier) + + super.init(nibName: nil, bundle: nil) + view.addSubview(tableView) + + let dataSource = ServerRuleSection.tableViewDiffableDataSource(tableView: tableView) + + tableView.dataSource = dataSource + self.dataSource = dataSource + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: tableView.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + + func update(with instance: Mastodon.Entity.V2.Instance) { + guard let dataSource, let rules = instance.rules, rules.isNotEmpty else { return } + + var snapshot = NSDiffableDataSourceSnapshot() + + snapshot.appendSections([.rules]) + let ruleItems = rules.enumerated().compactMap { index, rule in ServerRuleItem.rule(index: index, rule: rule) } + snapshot.appendItems(ruleItems, toSection: .rules) + + dataSource.apply(snapshot) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/ServerDetailsViewController.swift b/Mastodon/Scene/Settings/Server Details/ServerDetailsViewController.swift new file mode 100644 index 000000000..fb724f8f9 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/ServerDetailsViewController.swift @@ -0,0 +1,165 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonSDK +import MastodonLocalization +import MetaTextKit + +enum ServerDetailsTab: Int, CaseIterable { + case about = 0 + case rules = 1 + + var title: String { + switch self { + case .about: return L10n.Scene.Settings.ServerDetails.about + case .rules: return L10n.Scene.Settings.ServerDetails.rules + } + } +} + +protocol ServerDetailsViewControllerDelegate: AnyObject {} + +class ServerDetailsViewController: UIViewController { + + weak var delegate: (ServerDetailsViewControllerDelegate & AboutInstanceViewControllerDelegate & InstanceRulesViewControllerDelegate & MetaLabelDelegate)? { + didSet { + aboutInstanceViewController.delegate = delegate + instanceRulesViewController.delegate = delegate + aboutInstanceViewController.footerView.contentLabel.linkDelegate = delegate + } + } + let pageController: UIPageViewController + + private let segmentedControlWrapper: UIView + let segmentedControl: UISegmentedControl + let aboutInstanceViewController: AboutInstanceViewController + let instanceRulesViewController: InstanceRulesViewController + let containerView: UIView + + init(domain: String) { + segmentedControl = UISegmentedControl() + segmentedControl.translatesAutoresizingMaskIntoConstraints = false + + segmentedControlWrapper = UIView() + segmentedControlWrapper.translatesAutoresizingMaskIntoConstraints = false + segmentedControlWrapper.addSubview(segmentedControl) + + containerView = UIView() + containerView.translatesAutoresizingMaskIntoConstraints = false + + aboutInstanceViewController = AboutInstanceViewController() + instanceRulesViewController = InstanceRulesViewController() + + pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) + pageController.setViewControllers([aboutInstanceViewController], direction: .forward, animated: false) + pageController.view.translatesAutoresizingMaskIntoConstraints = false + + super.init(nibName: nil, bundle: nil) + + view.addSubview(segmentedControlWrapper) + view.addSubview(containerView) + view.backgroundColor = .systemGroupedBackground + + containerView.addSubview(pageController.view) + addChild(pageController) + pageController.didMove(toParent: self) + pageController.delegate = self + pageController.dataSource = self + + ServerDetailsTab.allCases.forEach { + segmentedControl.insertSegment(withTitle: $0.title, at: $0.rawValue, animated: false) + } + segmentedControl.selectedSegmentIndex = ServerDetailsTab.about.rawValue + segmentedControl.addTarget(self, action: #selector(ServerDetailsViewController.segmentedControlValueChanged(_:)), for: .valueChanged) + + setupConstraints() + + title = domain + + setupNavigationBarAppearance() + setupNavigationBarBackgroundView() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + segmentedControlWrapper.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + segmentedControlWrapper.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: segmentedControlWrapper.trailingAnchor), + + containerView.topAnchor.constraint(equalTo: segmentedControlWrapper.bottomAnchor), + containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), + + segmentedControl.topAnchor.constraint(equalTo: segmentedControlWrapper.topAnchor, constant: 4), + segmentedControl.leadingAnchor.constraint(equalTo: segmentedControlWrapper.leadingAnchor, constant: 16), + segmentedControlWrapper.trailingAnchor.constraint(equalTo: segmentedControl.trailingAnchor, constant: 16), + segmentedControlWrapper.bottomAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8), + + pageController.view.topAnchor.constraint(equalTo: containerView.topAnchor), + pageController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + containerView.trailingAnchor.constraint(equalTo: pageController.view.trailingAnchor), + containerView.bottomAnchor.constraint(equalTo: pageController.view.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + + //MARK: - Actions + @objc + func segmentedControlValueChanged(_ sender: UISegmentedControl) { + guard let selectedTab = ServerDetailsTab(rawValue: sender.selectedSegmentIndex) else { return } + + switch selectedTab { + case .about: + pageController.setViewControllers([aboutInstanceViewController], direction: .reverse, animated: true) + case .rules: + pageController.setViewControllers([instanceRulesViewController], direction: .forward, animated: true) + } + } + + func update(with instance: Mastodon.Entity.V2.Instance) { + aboutInstanceViewController.update(with: instance) + instanceRulesViewController.update(with: instance) + } + + func updateFooter(with extendedDescription: Mastodon.Entity.ExtendedDescription) { + aboutInstanceViewController.updateFooter(with: extendedDescription) + } +} + +//MARK: - UIPageViewControllerDataSource +extension ServerDetailsViewController: UIPageViewControllerDataSource { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + if viewController == instanceRulesViewController { + return aboutInstanceViewController + } else { + return nil + } + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + if viewController == aboutInstanceViewController { + return instanceRulesViewController + } else { + return nil + } + } +} + +//MARK: - UIPageViewControllerDelegate +extension ServerDetailsViewController: UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + guard let currentViewController = pageViewController.viewControllers?.first else { return } + + if currentViewController == aboutInstanceViewController { + segmentedControl.selectedSegmentIndex = ServerDetailsTab.about.rawValue + } else if currentViewController == instanceRulesViewController { + segmentedControl.selectedSegmentIndex = ServerDetailsTab.rules.rawValue + } + } +} + +extension ServerDetailsViewController: OnboardingViewControllerAppearance {} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift new file mode 100644 index 000000000..e0d5b61ba --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift @@ -0,0 +1,18 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation +import MastodonSDK +import MastodonLocalization + +enum AboutInstanceSection: Int, Hashable { + case main = 0 + + var title: String { + return L10n.Scene.Settings.ServerDetails.AboutInstance.title + } +} + +enum AboutInstanceItem: Hashable { + case adminAccount(Mastodon.Entity.Account) + case contactAdmin(String) +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableFooterView.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableFooterView.swift new file mode 100644 index 000000000..4118e1e40 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableFooterView.swift @@ -0,0 +1,68 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MetaTextKit +import MastodonSDK +import MastodonMeta +import MastodonCore +import MastodonAsset +import MastodonLocalization + +class AboutInstanceTableFooterView: UIView { + let headlineLabel: UILabel + let contentLabel: MetaLabel + + init() { + + headlineLabel = UILabel() + headlineLabel.translatesAutoresizingMaskIntoConstraints = false + headlineLabel.font = UIFontMetrics(forTextStyle: .title2).scaledFont(for: .systemFont(ofSize: 22, weight: .bold)) + + contentLabel = MetaLabel(style: .aboutInstance) + contentLabel.numberOfLines = 0 + contentLabel.translatesAutoresizingMaskIntoConstraints = false + super.init(frame: .zero) + + addSubview(headlineLabel) + addSubview(contentLabel) + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + + let horizontalMargin = 16.0 + let verticalMargin = 24.0 + + let constraints = [ + headlineLabel.topAnchor.constraint(equalTo: topAnchor, constant: verticalMargin), + headlineLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalMargin), + trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor, constant: horizontalMargin), + + contentLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: 8), + contentLabel.leadingAnchor.constraint(equalTo: headlineLabel.leadingAnchor), + contentLabel.trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor), + bottomAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: verticalMargin), + ] + + NSLayoutConstraint.activate(constraints) + } + + func update(with extendedDescription: Mastodon.Entity.ExtendedDescription) { + headlineLabel.text = L10n.Scene.Settings.ServerDetails.AboutInstance.legalNotice + + let content = extendedDescription.content + .replacingOccurrences(of: "
", with: "\n") + .replacingOccurrences(of: "\n\n", with: "\n") + + + if let metaContent = try? MastodonMetaContent.convert(document: MastodonContent(content: content, emojis: [:])) { + contentLabel.configure(content: metaContent) + } else { + let content = PlaintextMetaContent(string: content) + contentLabel.configure(content: content) + } + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewDataSource.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewDataSource.swift new file mode 100644 index 000000000..508097119 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewDataSource.swift @@ -0,0 +1,16 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +class AboutInstanceTableViewDataSource: UITableViewDiffableDataSource { + + override init(tableView: UITableView, cellProvider: @escaping UITableViewDiffableDataSource.CellProvider) { + super.init(tableView: tableView, cellProvider: cellProvider) + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + guard let section = AboutInstanceSection(rawValue: section) else { return nil } + + return section.title.uppercased() + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewHeader.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewHeader.swift new file mode 100644 index 000000000..0e8d731f1 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstanceTableViewHeader.swift @@ -0,0 +1,40 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonAsset +import AlamofireImage + +class AboutInstanceTableHeaderView: UIView { + let thumbnailImageView: UIImageView + + init() { + thumbnailImageView = UIImageView(image: Asset.Settings.aboutInstancePlaceholder.image) + thumbnailImageView.contentMode = .scaleAspectFill + thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false + + super.init(frame: .zero) + + addSubview(thumbnailImageView) + + setupConstraints() + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + thumbnailImageView.topAnchor.constraint(equalTo: topAnchor), + thumbnailImageView.leadingAnchor.constraint(equalTo: leadingAnchor), + trailingAnchor.constraint(equalTo: thumbnailImageView.trailingAnchor), + bottomAnchor.constraint(equalTo: thumbnailImageView.bottomAnchor, constant: 16), + ] + + NSLayoutConstraint.activate(constraints) + } + + func updateImage(with thumbnailURL: URL, completion: (() -> Void)? = nil) { + thumbnailImageView.af.setImage(withURL: thumbnailURL, placeholderImage: Asset.Settings.aboutInstancePlaceholder.image, completion: { _ in + completion?() + }) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift new file mode 100644 index 000000000..80acbc3d1 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift @@ -0,0 +1,5 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +typealias AdminTableViewCell = SearchResultsProfileTableViewCell diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift new file mode 100644 index 000000000..69ffaef86 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift @@ -0,0 +1,22 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonAsset +import MastodonLocalization + +class ContactAdminTableViewCell: UITableViewCell { + + static let reuseIdentifier = "ContactAdminTableViewCell" + + func configure() { + var configuration = defaultContentConfiguration() + + configuration.textProperties.color = Asset.Colors.Brand.blurple.color + configuration.image = UIImage(systemName: "envelope") + configuration.imageProperties.tintColor = Asset.Colors.Brand.blurple.color + configuration.text = L10n.Scene.Settings.ServerDetails.AboutInstance.messageAdmin + backgroundColor = .secondarySystemGroupedBackground + + contentConfiguration = configuration + } +} diff --git a/Mastodon/Scene/Settings/Settings Overview/Settings.swift b/Mastodon/Scene/Settings/Settings Overview/Settings.swift index bf3fba0bd..64c4c3ad3 100644 --- a/Mastodon/Scene/Settings/Settings Overview/Settings.swift +++ b/Mastodon/Scene/Settings/Settings Overview/Settings.swift @@ -10,62 +10,78 @@ struct SettingsSection: Hashable { enum SettingsEntry: Hashable { case general case notifications + case serverDetails(domain: String) case aboutMastodon case logout(accountName: String) var title: String { switch self { - case .general: - return L10n.Scene.Settings.Overview.general - case .notifications: - return L10n.Scene.Settings.Overview.notifications - case .aboutMastodon: - return L10n.Scene.Settings.Overview.aboutMastodon - case .logout(let accountName): - return L10n.Scene.Settings.Overview.logout(accountName) + case .general: + return L10n.Scene.Settings.Overview.general + case .notifications: + return L10n.Scene.Settings.Overview.notifications + case .serverDetails(_): + return L10n.Scene.Settings.Overview.serverDetails + case .aboutMastodon: + return L10n.Scene.Settings.Overview.aboutMastodon + case .logout(let accountName): + return L10n.Scene.Settings.Overview.logout(accountName) + } + } + + var secondaryTitle: String? { + switch self { + case .serverDetails(domain: let domain): + return domain + case .general, .notifications, .aboutMastodon, .logout(_): + return nil } } var accessoryType: UITableViewCell.AccessoryType { switch self { - case .general, .notifications, .aboutMastodon, .logout(_): - return .disclosureIndicator + case .general, .notifications, .serverDetails(_), .aboutMastodon, .logout(_): + return .disclosureIndicator } } var icon: UIImage? { switch self { - case .general: - return UIImage(systemName: "gear") - case .notifications: - return UIImage(systemName: "bell.badge") - case .aboutMastodon: - return UIImage(systemName: "info.circle.fill") - case .logout(_): - return nil + case .general: + return UIImage(systemName: "gear") + case .notifications: + return UIImage(systemName: "bell.badge") + case .serverDetails(_): + return UIImage(systemName: "server.rack") + case .aboutMastodon: + return UIImage(systemName: "info.circle.fill") + case .logout(_): + return nil } } var iconBackgroundColor: UIColor? { switch self { - case .general: - return .systemGray - case .notifications: - return .systemRed - case .aboutMastodon: - return .systemPurple - case .logout(_): - return nil + case .general: + return .systemGray + case .notifications: + return .systemRed + case .serverDetails(_): + return .systemTeal + case .aboutMastodon: + return .systemPurple + case .logout(_): + return nil } } var textColor: UIColor { switch self { - case .general, .notifications, .aboutMastodon: - return .label - case .logout(_): - return .red + case .general, .notifications, .aboutMastodon, .serverDetails(_): + return .label + case .logout(_): + return .red } } diff --git a/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift b/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift index 7e09f0fce..fcb181320 100644 --- a/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift +++ b/Mastodon/Scene/Settings/Settings Overview/SettingsTableViewCell.swift @@ -9,7 +9,9 @@ class SettingsTableViewCell: UITableViewCell { let iconImageView: UIImageView let iconImageBackgroundView: UIView let titleLabel: UILabel + let secondaryLabel: UILabel + private let labelStackView: UIStackView private let contentStackView: UIStackView override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -21,8 +23,17 @@ class SettingsTableViewCell: UITableViewCell { iconImageBackgroundView.addSubview(iconImageView) titleLabel = UILabel() + titleLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) + secondaryLabel = UILabel() + secondaryLabel.font = UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 12, weight: .regular)) + secondaryLabel.textColor = .secondaryLabel - contentStackView = UIStackView(arrangedSubviews: [iconImageBackgroundView, titleLabel]) + labelStackView = UIStackView(arrangedSubviews: [titleLabel, secondaryLabel]) + labelStackView.axis = .vertical + labelStackView.spacing = 2 + labelStackView.alignment = .leading + + contentStackView = UIStackView(arrangedSubviews: [iconImageBackgroundView, labelStackView]) contentStackView.translatesAutoresizingMaskIntoConstraints = false contentStackView.axis = .horizontal contentStackView.alignment = .center @@ -50,8 +61,8 @@ class SettingsTableViewCell: UITableViewCell { iconImageView.centerXAnchor.constraint(equalTo: iconImageBackgroundView.centerXAnchor), iconImageView.widthAnchor.constraint(equalToConstant: 20), - titleLabel.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 12), - contentView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLabel.bottomAnchor, constant: 12), + labelStackView.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 12), + contentView.bottomAnchor.constraint(greaterThanOrEqualTo: labelStackView.bottomAnchor, constant: 12), ] NSLayoutConstraint.activate(constraints) @@ -61,6 +72,13 @@ class SettingsTableViewCell: UITableViewCell { titleLabel.textColor = entry.textColor titleLabel.text = entry.title + if let secondaryTitle = entry.secondaryTitle { + secondaryLabel.isHidden = false + secondaryLabel.text = secondaryTitle + } else { + secondaryLabel.isHidden = true + } + if let icon = entry.icon { iconImageView.image = icon iconImageView.tintColor = .white @@ -73,7 +91,6 @@ class SettingsTableViewCell: UITableViewCell { iconImageBackgroundView.backgroundColor = entry.iconBackgroundColor accessoryType = entry.accessoryType - } } diff --git a/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift b/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift index 5c8727110..8dab17a63 100644 --- a/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/Settings Overview/SettingsViewController.swift @@ -16,11 +16,11 @@ class SettingsViewController: UIViewController { var tableViewDataSource: UITableViewDiffableDataSource? let tableView: UITableView - init(accountName: String) { + init(accountName: String, domain: String) { sections = [ .init(entries: [.general, .notifications]), - .init(entries: [.aboutMastodon]), + .init(entries: [.serverDetails(domain: domain), .aboutMastodon]), .init(entries: [.logout(accountName: accountName)]) ] diff --git a/Mastodon/Scene/Settings/SettingsCoordinator.swift b/Mastodon/Scene/Settings/SettingsCoordinator.swift index a8415303b..2c4973a30 100644 --- a/Mastodon/Scene/Settings/SettingsCoordinator.swift +++ b/Mastodon/Scene/Settings/SettingsCoordinator.swift @@ -6,6 +6,7 @@ import MastodonCore import CoreDataStack import MastodonSDK import Combine +import MetaTextKit protocol SettingsCoordinatorDelegate: AnyObject { func logout(_ settingsCoordinator: SettingsCoordinator) @@ -26,15 +27,17 @@ class SettingsCoordinator: NSObject, Coordinator { let appContext: AppContext let authContext: AuthContext var disposeBag = Set() + let sceneCoordinator: SceneCoordinator - init(presentedOn: UIViewController, accountName: String, setting: Setting, appContext: AppContext, authContext: AuthContext) { + init(presentedOn: UIViewController, accountName: String, setting: Setting, appContext: AppContext, authContext: AuthContext, sceneCoordinator: SceneCoordinator) { self.presentedOn = presentedOn navigationController = UINavigationController() self.setting = setting self.appContext = appContext self.authContext = authContext + self.sceneCoordinator = sceneCoordinator - settingsViewController = SettingsViewController(accountName: accountName) + settingsViewController = SettingsViewController(accountName: accountName, domain: authContext.mastodonAuthenticationBox.domain) } func start() { @@ -65,8 +68,29 @@ extension SettingsCoordinator: SettingsViewControllerDelegate { let notificationViewController = NotificationSettingsViewController(currentSetting: currentSetting, notificationsEnabled: notificationsEnabled) notificationViewController.delegate = self - self.navigationController.pushViewController(notificationViewController, animated: true) + navigationController.pushViewController(notificationViewController, animated: true) + case .serverDetails(let domain): + let serverDetailsViewController = ServerDetailsViewController(domain: domain) + serverDetailsViewController.delegate = self + appContext.apiService.instanceV2(domain: domain) + .sink { _ in + + } receiveValue: { content in + serverDetailsViewController.update(with: content.value) + } + .store(in: &disposeBag) + + appContext.apiService.extendedDescription(domain: domain) + .sink { _ in + + } receiveValue: { content in + serverDetailsViewController.updateFooter(with: content.value) + } + .store(in: &disposeBag) + + + navigationController.pushViewController(serverDetailsViewController, animated: true) case .aboutMastodon: let aboutViewController = AboutViewController() aboutViewController.delegate = self @@ -135,6 +159,8 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { guard viewModel.updated else { return } + //Show spinner? + let authenticationBox = authContext.mastodonAuthenticationBox guard let subscription = setting.activeSubscription, setting.domain == authenticationBox.domain, @@ -168,16 +194,10 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { }) .store(in: &disposeBag) } - + func showNotificationSettings(_ viewController: UIViewController) { - if #available(iOS 16.0, *) { - if let url = URL(string: UIApplication.openNotificationSettingsURLString) { - UIApplication.shared.open(url) - } - } else { - if let url = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(url) - } + if let url = URL(string: UIApplication.openNotificationSettingsURLString) { + UIApplication.shared.open(url) } } } @@ -189,3 +209,58 @@ extension SettingsCoordinator: PolicySelectionViewControllerDelegate { try? self.appContext.managedObjectContext.save() } } + +//MARK: - ServerDetailsViewControllerDelegate +extension SettingsCoordinator: ServerDetailsViewControllerDelegate { + +} + +extension SettingsCoordinator: AboutInstanceViewControllerDelegate { + @MainActor func showAdminAccount(_ viewController: AboutInstanceViewController, account: Mastodon.Entity.Account) { + Task { + let user = try await appContext.apiService.fetchUser(username: account.username, domain: authContext.mastodonAuthenticationBox.domain, authenticationBox: authContext.mastodonAuthenticationBox) + + let profileViewModel = ProfileViewModel(context: appContext, authContext: authContext, optionalMastodonUser: user) + + _ = await MainActor.run { + sceneCoordinator.present(scene: .profile(viewModel: profileViewModel), transition: .show) + } + } + } + + func sendEmailToAdmin(_ viewController: AboutInstanceViewController, emailAddress: String) { + if let emailUrl = URL(string: "mailto:\(emailAddress)"), UIApplication.shared.canOpenURL(emailUrl) { + UIApplication.shared.open(emailUrl) + } + } +} + +extension SettingsCoordinator: InstanceRulesViewControllerDelegate { + +} + +extension SettingsCoordinator: MetaLabelDelegate { + @MainActor + func metaLabel(_ metaLabel: MetaLabel, didSelectMeta meta: Meta) { + switch meta { + case .url(_, _, let url, _): + guard let url = URL(string: url) else { return } + _ = sceneCoordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) + case .mention(_, _, let userInfo): + guard let href = userInfo?["href"] as? String, + let url = URL(string: href) else { return } + _ = sceneCoordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil)) + case .hashtag(_, let hashtag, _): + let hashtagTimelineViewModel = HashtagTimelineViewModel(context: appContext, authContext: authContext, hashtag: hashtag) + _ = sceneCoordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: nil, transition: .show) + case .email(let email, _): + if let emailUrl = URL(string: "mailto:\(email)"), UIApplication.shared.canOpenURL(emailUrl) { + UIApplication.shared.open(emailUrl) + } + case .emoji: + break + } + } + + +} diff --git a/MastodonSDK/Package.resolved b/MastodonSDK/Package.resolved deleted file mode 100644 index 39f40fa17..000000000 --- a/MastodonSDK/Package.resolved +++ /dev/null @@ -1,239 +0,0 @@ -{ - "pins" : [ - { - "identity" : "alamofire", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/Alamofire.git", - "state" : { - "revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b", - "version" : "5.6.2" - } - }, - { - "identity" : "alamofireimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/AlamofireImage.git", - "state" : { - "revision" : "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10", - "version" : "4.2.0" - } - }, - { - "identity" : "commonoslog", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MainasuK/CommonOSLog", - "state" : { - "revision" : "c121624a30698e9886efe38aebb36ff51c01b6c2", - "version" : "0.1.1" - } - }, - { - "identity" : "faviconfinder", - "kind" : "remoteSourceControl", - "location" : "https://github.com/will-lumley/FaviconFinder.git", - "state" : { - "revision" : "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a", - "version" : "3.3.0" - } - }, - { - "identity" : "flanimatedimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Flipboard/FLAnimatedImage.git", - "state" : { - "revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f", - "version" : "1.0.17" - } - }, - { - "identity" : "fpsindicator", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MainasuK/FPSIndicator.git", - "state" : { - "revision" : "e4a5067ccd5293b024c767f09e51056afd4a4796", - "version" : "1.1.0" - } - }, - { - "identity" : "fuzi", - "kind" : "remoteSourceControl", - "location" : "https://github.com/cezheng/Fuzi.git", - "state" : { - "revision" : "f08c8323da21e985f3772610753bcfc652c2103f", - "version" : "3.1.3" - } - }, - { - "identity" : "keychainaccess", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git", - "state" : { - "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7", - "version" : "4.2.2" - } - }, - { - "identity" : "metatextkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/TwidereProject/MetaTextKit.git", - "state" : { - "revision" : "dcd5255d6930c2fab408dc8562c577547e477624", - "version" : "2.2.5" - } - }, - { - "identity" : "nextlevelsessionexporter", - "kind" : "remoteSourceControl", - "location" : "https://github.com/NextLevel/NextLevelSessionExporter.git", - "state" : { - "revision" : "b6c0cce1aa37fe1547d694f958fac3c3524b74da", - "version" : "0.4.6" - } - }, - { - "identity" : "nuke", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kean/Nuke.git", - "state" : { - "revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97", - "version" : "10.11.2" - } - }, - { - "identity" : "nuke-flanimatedimage-plugin", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", - "state" : { - "revision" : "b59c346a7d536336db3b0f12c72c6e53ee709e16", - "version" : "8.0.0" - } - }, - { - "identity" : "pageboy", - "kind" : "remoteSourceControl", - "location" : "https://github.com/uias/Pageboy", - "state" : { - "revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5", - "version" : "3.7.0" - } - }, - { - "identity" : "panmodal", - "kind" : "remoteSourceControl", - "location" : "https://github.com/slackhq/PanModal.git", - "state" : { - "revision" : "b012aecb6b67a8e46369227f893c12544846613f", - "version" : "1.2.7" - } - }, - { - "identity" : "sdwebimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SDWebImage/SDWebImage.git", - "state" : { - "revision" : "9248fe561a2a153916fb9597e3af4434784c6d32", - "version" : "5.13.4" - } - }, - { - "identity" : "stripes", - "kind" : "remoteSourceControl", - "location" : "https://github.com/eneko/Stripes.git", - "state" : { - "revision" : "d533fd44b8043a3abbf523e733599173d6f98c11", - "version" : "0.2.0" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections.git", - "state" : { - "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", - "version" : "1.0.3" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio.git", - "state" : { - "revision" : "546610d52b19be3e19935e0880bb06b9c03f5cef", - "version" : "1.14.4" - } - }, - { - "identity" : "swift-nio-zlib-support", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-zlib-support.git", - "state" : { - "revision" : "37760e9a52030bb9011972c5213c3350fa9d41fd", - "version" : "1.0.0" - } - }, - { - "identity" : "swiftsoup", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scinfu/SwiftSoup.git", - "state" : { - "revision" : "6778575285177365cbad3e5b8a72f2a20583cfec", - "version" : "2.4.3" - } - }, - { - "identity" : "swiftui-introspect", - "kind" : "remoteSourceControl", - "location" : "https://github.com/siteline/SwiftUI-Introspect.git", - "state" : { - "revision" : "f2616860a41f9d9932da412a8978fec79c06fe24", - "version" : "0.1.4" - } - }, - { - "identity" : "tabbarpager", - "kind" : "remoteSourceControl", - "location" : "https://github.com/TwidereProject/TabBarPager.git", - "state" : { - "revision" : "488aa66d157a648901b61721212c0dec23d27ee5", - "version" : "0.1.0" - } - }, - { - "identity" : "tabman", - "kind" : "remoteSourceControl", - "location" : "https://github.com/uias/Tabman", - "state" : { - "revision" : "4a4f7c755b875ffd4f9ef10d67a67883669d2465", - "version" : "2.13.0" - } - }, - { - "identity" : "tocropviewcontroller", - "kind" : "remoteSourceControl", - "location" : "https://github.com/TimOliver/TOCropViewController.git", - "state" : { - "revision" : "d0470491f56e734731bbf77991944c0dfdee3e0e", - "version" : "2.6.1" - } - }, - { - "identity" : "uihostingconfigurationbackport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/woxtu/UIHostingConfigurationBackport.git", - "state" : { - "revision" : "6091f2d38faa4b24fc2ca0389c651e2f666624a3", - "version" : "0.1.0" - } - }, - { - "identity" : "uitextview-placeholder", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MainasuK/UITextView-Placeholder.git", - "state" : { - "revision" : "20f513ded04a040cdf5467f0891849b1763ede3b", - "version" : "1.4.1" - } - } - ], - "version" : 2 -} diff --git a/MastodonSDK/Package.swift b/MastodonSDK/Package.swift index 7b44f806d..a6ac785e7 100644 --- a/MastodonSDK/Package.swift +++ b/MastodonSDK/Package.swift @@ -18,7 +18,7 @@ let package = Package( name: "MastodonSDK", defaultLocalization: "en", platforms: [ - .iOS(.v15), + .iOS(.v16), ], products: [ // Static Library @@ -42,7 +42,6 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.3"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), .package(url: "https://github.com/Flipboard/FLAnimatedImage.git", from: "1.0.0"), - .package(url: "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", from: "8.0.0"), .package(url: "https://github.com/kean/Nuke.git", from: "10.3.1"), .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2"), .package(url: "https://github.com/slackhq/PanModal.git", from: "1.2.7"), @@ -54,6 +53,7 @@ let package = Package( .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.12.0"), .package(url: "https://github.com/eneko/Stripes.git", from: "0.2.0"), .package(url: "https://github.com/NextLevel/NextLevelSessionExporter.git", from: "0.4.6"), + .package(url: "https://github.com/xmartlabs/XLPagerTabStrip.git", from: "9.1.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -126,6 +126,8 @@ let package = Package( .product(name: "PanModal", package: "PanModal"), .product(name: "Stripes", package: "Stripes"), .product(name: "NextLevelSessionExporter", package: "NextLevelSessionExporter"), + .product(name: "SDWebImage", package: "SDWebImage"), + .product(name: "XLPagerTabStrip", package: "XLPagerTabStrip"), ] ), .testTarget( diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/Contents.json similarity index 73% rename from MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/Contents.json rename to MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/Contents.json index 45265b0c2..95dc59919 100644 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/Contents.json +++ b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "dark.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "dark@2x.png", + "filename" : "about_instance_placeholder@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "dark@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/about_instance_placeholder@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/about_instance_placeholder@2x.png new file mode 100644 index 000000000..d40e27a12 Binary files /dev/null and b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/about_instance_placeholder.imageset/about_instance_placeholder@2x.png differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/Contents.json deleted file mode 100644 index 634b1b249..000000000 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "automatic.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "automatic@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "automatic@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic.png deleted file mode 100644 index 8a4997026..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@2x.png deleted file mode 100644 index 353f123a5..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@2x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@3x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@3x.png deleted file mode 100644 index 6a865417a..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/automatic.imageset/automatic@3x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark.png deleted file mode 100644 index 5bfe79abf..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@2x.png deleted file mode 100644 index 4bbee3622..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@2x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@3x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@3x.png deleted file mode 100644 index 4ea316fb2..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/dark.imageset/dark@3x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/Contents.json deleted file mode 100644 index c89e6245e..000000000 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "light@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light.png deleted file mode 100644 index 23efb384c..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@2x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@2x.png deleted file mode 100644 index 6bff099c8..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@2x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@3x.png b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@3x.png deleted file mode 100644 index 94c68eb50..000000000 Binary files a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Settings/light.imageset/light@3x.png and /dev/null differ diff --git a/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift b/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift index d23979b95..be8f204e0 100644 --- a/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift +++ b/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift @@ -219,9 +219,7 @@ public enum Asset { } } public enum Settings { - public static let automatic = ImageAsset(name: "Settings/automatic") - public static let dark = ImageAsset(name: "Settings/dark") - public static let light = ImageAsset(name: "Settings/light") + public static let aboutInstancePlaceholder = ImageAsset(name: "Settings/about_instance_placeholder") } public enum Theme { public enum System { diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift index b5a549903..03a63461e 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Instance.swift @@ -24,4 +24,8 @@ extension APIService { ) -> AnyPublisher, Error> { return Mastodon.API.V2.Instance.instance(session: session, domain: domain) } + + public func extendedDescription(domain: String) -> AnyPublisher, Error> { + return Mastodon.API.Instance.extendedDescription(session: session, domain: domain) + } } diff --git a/MastodonSDK/Sources/MastodonExtension/ImageAnalyzer.swift b/MastodonSDK/Sources/MastodonExtension/ImageAnalyzer.swift index cf8a0f437..0ee05391b 100644 --- a/MastodonSDK/Sources/MastodonExtension/ImageAnalyzer.swift +++ b/MastodonSDK/Sources/MastodonExtension/ImageAnalyzer.swift @@ -7,7 +7,6 @@ import VisionKit -@available(iOS 16.0, *) extension ImageAnalyzer { public static let shared = ImageAnalyzer() } diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index 05ea58f05..af79705c1 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -1418,7 +1418,7 @@ public enum L10n { public enum Settings { public enum AboutMastodon { /// Clear Media Storage - public static let cleaerMediaStorage = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.CleaerMediaStorage", fallback: "Clear Media Storage") + public static let clearMediaStorage = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.ClearMediaStorage", fallback: "Clear Media Storage") /// Contribute to Mastodon public static let contributeToMastodon = L10n.tr("Localizable", "Scene.Settings.AboutMastodon.ContributeToMastodon", fallback: "Contribute to Mastodon") /// Even More Settings @@ -1509,6 +1509,8 @@ public enum L10n { } /// Notifications public static let notifications = L10n.tr("Localizable", "Scene.Settings.Overview.Notifications", fallback: "Notifications") + /// Server Details + public static let serverDetails = L10n.tr("Localizable", "Scene.Settings.Overview.ServerDetails", fallback: "Server Details") /// Support Mastodon public static let supportMastodon = L10n.tr("Localizable", "Scene.Settings.Overview.SupportMastodon", fallback: "Support Mastodon") /// Settings @@ -1592,6 +1594,20 @@ public enum L10n { public static let title = L10n.tr("Localizable", "Scene.Settings.Section.SpicyZone.Title", fallback: "The Spicy Zone") } } + public enum ServerDetails { + /// About + public static let about = L10n.tr("Localizable", "Scene.Settings.ServerDetails.About", fallback: "About") + /// Rules + public static let rules = L10n.tr("Localizable", "Scene.Settings.ServerDetails.Rules", fallback: "Rules") + public enum AboutInstance { + /// A legal notice + public static let legalNotice = L10n.tr("Localizable", "Scene.Settings.ServerDetails.AboutInstance.LegalNotice", fallback: "A legal notice") + /// Message Admin + public static let messageAdmin = L10n.tr("Localizable", "Scene.Settings.ServerDetails.AboutInstance.MessageAdmin", fallback: "Message Admin") + /// Administrator + public static let title = L10n.tr("Localizable", "Scene.Settings.ServerDetails.AboutInstance.Title", fallback: "Administrator") + } + } } public enum SuggestionAccount { /// Follow all diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings index ab6fef20f..509de54c4 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -527,6 +527,7 @@ uploaded to Mastodon."; "Scene.Settings.Overview.General" = "General"; "Scene.Settings.Overview.Notifications" = "Notifications"; "Scene.Settings.Overview.SupportMastodon" = "Support Mastodon"; +"Scene.Settings.Overview.ServerDetails" = "Server Details"; "Scene.Settings.Overview.AboutMastodon" = "About Mastodon"; "Scene.Settings.Overview.Logout" = "Logout %@"; @@ -534,7 +535,13 @@ uploaded to Mastodon."; "Scene.Settings.AboutMastodon.MoreSettings" = "Even More Settings"; "Scene.Settings.AboutMastodon.ContributeToMastodon" = "Contribute to Mastodon"; "Scene.Settings.AboutMastodon.PrivacyPolicy" = "Privacy Policy"; -"Scene.Settings.AboutMastodon.CleaerMediaStorage" = "Clear Media Storage"; +"Scene.Settings.AboutMastodon.ClearMediaStorage" = "Clear Media Storage"; + +"Scene.Settings.ServerDetails.About" = "About"; +"Scene.Settings.ServerDetails.Rules" = "Rules"; +"Scene.Settings.ServerDetails.AboutInstance.Title" = "Administrator"; +"Scene.Settings.ServerDetails.AboutInstance.MessageAdmin" = "Message Admin"; +"Scene.Settings.ServerDetails.AboutInstance.LegalNotice" = "A legal notice"; "Scene.Settings.General.Title" = "General"; "Scene.Settings.General.Appearance.SectionTitle" = "Appearance"; diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings index ab6fef20f..509de54c4 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings @@ -527,6 +527,7 @@ uploaded to Mastodon."; "Scene.Settings.Overview.General" = "General"; "Scene.Settings.Overview.Notifications" = "Notifications"; "Scene.Settings.Overview.SupportMastodon" = "Support Mastodon"; +"Scene.Settings.Overview.ServerDetails" = "Server Details"; "Scene.Settings.Overview.AboutMastodon" = "About Mastodon"; "Scene.Settings.Overview.Logout" = "Logout %@"; @@ -534,7 +535,13 @@ uploaded to Mastodon."; "Scene.Settings.AboutMastodon.MoreSettings" = "Even More Settings"; "Scene.Settings.AboutMastodon.ContributeToMastodon" = "Contribute to Mastodon"; "Scene.Settings.AboutMastodon.PrivacyPolicy" = "Privacy Policy"; -"Scene.Settings.AboutMastodon.CleaerMediaStorage" = "Clear Media Storage"; +"Scene.Settings.AboutMastodon.ClearMediaStorage" = "Clear Media Storage"; + +"Scene.Settings.ServerDetails.About" = "About"; +"Scene.Settings.ServerDetails.Rules" = "Rules"; +"Scene.Settings.ServerDetails.AboutInstance.Title" = "Administrator"; +"Scene.Settings.ServerDetails.AboutInstance.MessageAdmin" = "Message Admin"; +"Scene.Settings.ServerDetails.AboutInstance.LegalNotice" = "A legal notice"; "Scene.Settings.General.Title" = "General"; "Scene.Settings.General.Appearance.SectionTitle" = "Appearance"; diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift index e90ee27c1..871f43632 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Instance.swift @@ -30,11 +30,7 @@ extension Mastodon.API.Instance { session: URLSession, domain: String ) -> AnyPublisher, Error> { - let request = Mastodon.API.get( - url: instanceEndpointURL(domain: domain), - query: nil, - authorization: nil - ) + let request = Mastodon.API.get(url: instanceEndpointURL(domain: domain)) return session.dataTaskPublisher(for: request) .tryMap { data, response in let value: Mastodon.Entity.Instance @@ -53,5 +49,27 @@ extension Mastodon.API.Instance { } .eraseToAnyPublisher() } - + + static func extendedDescriptionEndpointURL(domain: String) -> URL { + return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("instance").appendingPathComponent("extended_description") + } + + /// Extended description of the server + /// + /// - Returns: A ``MastodonSDK.Mastodon.Entity.ExtendedDescription`` + /// + /// ## Reference: + /// [Document](https://docs.joinmastodon.org/methods/instance/#extended_description) + public static func extendedDescription( + session: URLSession, + domain: String + ) -> AnyPublisher, Error> { + let request = Mastodon.API.get(url: extendedDescriptionEndpointURL(domain: domain)) + return session.dataTaskPublisher(for: request) + .tryMap { data, response in + let value = try Mastodon.API.decode(type: Mastodon.Entity.ExtendedDescription.self, from: data, response: response) + return Mastodon.Response.Content(value: value, response: response) + } + .eraseToAnyPublisher() + } } diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+ExtendedDescription.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+ExtendedDescription.swift new file mode 100644 index 000000000..c42fe6ab1 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+ExtendedDescription.swift @@ -0,0 +1,19 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation + +extension Mastodon.Entity { + /// Extended description of the server + + /// ## Reference: + /// [Document](https://docs.joinmastodon.org/entities/ExtendedDescription/) + public struct ExtendedDescription: Codable { + public let updatedAt: Date + public let content: String + + enum CodingKeys: String, CodingKey { + case updatedAt = "updated_at" + case content + } + } +} diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift index d662c24c2..3cfb29699 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+InstanceV2.swift @@ -10,12 +10,11 @@ extension Mastodon.Entity.V2 { /// # Reference /// [Document](https://docs.joinmastodon.org/entities/instance/) public struct Instance: Codable { - + public let domain: String? public let title: String public let description: String public let shortDescription: String? - public let email: String? public let version: String? public let languages: [String]? // (ISO 639 Part 1-5 language codes) public let registrations: Mastodon.Entity.V2.Instance.Registrations? @@ -25,7 +24,7 @@ extension Mastodon.Entity.V2 { public let statistics: Mastodon.Entity.Instance.Statistics? public let thumbnail: Thumbnail? - public let contactAccount: Mastodon.Entity.Account? + public let contact: Mastodon.Entity.V2.Instance.Contact? public let rules: [Mastodon.Entity.Instance.Rule]? // https://github.com/mastodon/mastodon/pull/16485 @@ -36,7 +35,7 @@ extension Mastodon.Entity.V2 { self.title = domain self.description = "" self.shortDescription = nil - self.email = "" + self.contact = nil self.version = nil self.languages = nil self.registrations = nil @@ -45,7 +44,6 @@ extension Mastodon.Entity.V2 { self.urls = nil self.statistics = nil self.thumbnail = nil - self.contactAccount = nil self.rules = nil self.configuration = nil } @@ -55,7 +53,6 @@ extension Mastodon.Entity.V2 { case title case description case shortDescription = "short_description" - case email case version case languages case registrations @@ -65,7 +62,7 @@ extension Mastodon.Entity.V2 { case statistics = "stats" case thumbnail - case contactAccount = "contact_account" + case contact case rules case configuration @@ -106,3 +103,10 @@ extension Mastodon.Entity.V2.Instance { public let url: String? } } + +extension Mastodon.Entity.V2.Instance { + public struct Contact: Codable { + public let email: String? + public let account: Mastodon.Entity.Account? + } +} diff --git a/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift b/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift index b352bdbc2..cee24e98b 100644 --- a/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift +++ b/MastodonSDK/Sources/MastodonUI/Extension/MetaLabel.swift @@ -30,6 +30,7 @@ extension MetaLabel { case accountListUsername case sidebarHeadline(isSelected: Bool) case sidebarSubheadline(isSelected: Bool) + case aboutInstance } public convenience init(style: Style) { @@ -129,6 +130,10 @@ extension MetaLabel { case .sidebarSubheadline(let isSelected): font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 13, weight: .regular), maximumPointSize: 18) textColor = isSelected ? .white : Asset.Colors.Label.secondary.color + case .aboutInstance: + font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) + textColor = .label + paragraphStyle.paragraphSpacing = 0 } self.font = font diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Poll/PollOptionRow.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Poll/PollOptionRow.swift index f10c094f4..4a033491e 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Poll/PollOptionRow.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Poll/PollOptionRow.swift @@ -44,45 +44,15 @@ public struct PollOptionRow: View { viewModel.textField?.becomeFirstResponder() } - if #available(iOS 16.0, *) { - field.accessibilityActions { - if let moveUp { - Button(L10n.Scene.Compose.Poll.moveUp, action: moveUp) - } - if let moveDown { - Button(L10n.Scene.Compose.Poll.moveDown, action: moveDown) - } - if let removeOption { - Button(L10n.Scene.Compose.Poll.removeOption, action: removeOption) - } + field.accessibilityActions { + if let moveUp { + Button(L10n.Scene.Compose.Poll.moveUp, action: moveUp) } - } else { - switch (moveUp, moveDown, removeOption) { - case let (.some(up), .some(down), .some(remove)): - field - .accessibilityAction(named: L10n.Scene.Compose.Poll.moveUp, up) - .accessibilityAction(named: L10n.Scene.Compose.Poll.moveDown, down) - .accessibilityAction(named: L10n.Scene.Compose.Poll.removeOption, remove) - case let (.some(up), .some(down), .none): - field - .accessibilityAction(named: L10n.Scene.Compose.Poll.moveUp, up) - .accessibilityAction(named: L10n.Scene.Compose.Poll.moveDown, down) - case let (.some(up), .none, .some(remove)): - field - .accessibilityAction(named: L10n.Scene.Compose.Poll.moveUp, up) - .accessibilityAction(named: L10n.Scene.Compose.Poll.removeOption, remove) - case let (.some(up), .none, .none): - field.accessibilityAction(named: L10n.Scene.Compose.Poll.moveUp, up) - case let (.none, .some(down), .some(remove)): - field - .accessibilityAction(named: L10n.Scene.Compose.Poll.moveDown, down) - .accessibilityAction(named: L10n.Scene.Compose.Poll.removeOption, remove) - case let (.none, .some(down), .none): - field.accessibilityAction(named: L10n.Scene.Compose.Poll.moveDown, down) - case let (.none, .none, .some(remove)): - field.accessibilityAction(named: L10n.Scene.Compose.Poll.removeOption, remove) - case (.none, .none, .none): - field + if let moveDown { + Button(L10n.Scene.Compose.Poll.moveDown, action: moveDown) + } + if let removeOption { + Button(L10n.Scene.Compose.Poll.removeOption, action: removeOption) } } } diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/ComposeContentToolbarView.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/ComposeContentToolbarView.swift index 70430f400..a5af2c10f 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/ComposeContentToolbarView.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/ComposeContentToolbarView.swift @@ -113,13 +113,7 @@ struct ComposeContentToolbarView: View { showingLanguagePicker = true } } label: { - let font: SwiftUI.Font = { - if #available(iOS 16, *) { - return .system(size: 11, weight: .semibold).width(viewModel.language.count == 3 ? .compressed : .standard) - } else { - return .system(size: 11, weight: .semibold) - } - }() + let font = SwiftUI.Font.system(size: 11, weight: .semibold).width(viewModel.language.count == 3 ? .compressed : .standard) Text(viewModel.language) .font(font) diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/LanguagePicker.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/LanguagePicker.swift index f51dd6732..10911c875 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/LanguagePicker.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Toolbar/LanguagePicker.swift @@ -37,15 +37,9 @@ struct LanguagePicker: View { return Text("") }() Button(action: { onSelect(lang.id) }) { - if #available(iOS 16.0, *) { - ViewThatFits(in: .horizontal) { - HStack(spacing: 0) { endonym; Text(" "); exonym } - VStack(alignment: .leading) { endonym; exonym } - } - } else { - // less optimal because if you’re using an LTR language, RTL languages - // will read as “ ([exonym])[endonym]” (and vice versa in RTL locales) - Text("\(endonym)\(exonym)") + ViewThatFits(in: .horizontal) { + HStack(spacing: 0) { endonym; Text(" "); exonym } + VStack(alignment: .leading) { endonym; exonym } } } .tint(.primary) diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift index be766f5e6..48089be64 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/CondensedUserView.swift @@ -163,7 +163,7 @@ public class CondensedUserView: UIView { } } - public func configure(with account: Mastodon.Entity.Account) { + public func configure(with account: Mastodon.Entity.Account, showFollowers: Bool = true) { let displayNameMetaContent: MetaContent do { let content = MastodonContent(content: account.displayNameWithFallback, emojis: account.emojis?.asDictionary ?? [:]) @@ -174,10 +174,16 @@ public class CondensedUserView: UIView { displayNameLabel.configure(content: displayNameMetaContent) acctLabel.text = account.acct - followersLabel.attributedText = NSAttributedString( - format: NSAttributedString(string: L10n.Common.UserList.followersCount("%@"), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))]), - args: NSAttributedString(string: Self.metricFormatter.string(from: account.followersCount) ?? account.followersCount.formatted(), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))]) - ) + + if showFollowers { + followersLabel.attributedText = NSAttributedString( + format: NSAttributedString(string: L10n.Common.UserList.followersCount("%@"), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))]), + args: NSAttributedString(string: Self.metricFormatter.string(from: account.followersCount) ?? account.followersCount.formatted(), attributes: [.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .bold))]) + ) + followersLabel.isHidden = false + } else { + followersLabel.isHidden = false + } avatarImageView.setImage(url: account.avatarImageURL()) diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift index b195315aa..ab8b449d4 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift @@ -328,13 +328,8 @@ extension StatusView.ViewModel { let paragraphStyle = statusView.contentMetaText.paragraphStyle if let language = language { - if #available(iOS 16, *) { - let direction = Locale.Language(identifier: language).characterDirection - paragraphStyle.alignment = direction == .rightToLeft ? .right : .left - } else { - let direction = Locale.characterDirection(forLanguage: language) - paragraphStyle.alignment = direction == .rightToLeft ? .right : .left - }; + let direction = Locale.Language(identifier: language).characterDirection + paragraphStyle.alignment = direction == .rightToLeft ? .right : .left } else { paragraphStyle.alignment = .natural } diff --git a/Podfile b/Podfile index 7a6e9b9ff..e9538aa3d 100644 --- a/Podfile +++ b/Podfile @@ -7,11 +7,6 @@ target 'Mastodon' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! - # Pods for Mastodon - - # UI - pod 'XLPagerTabStrip', '~> 9.0.0' - # misc pod 'SwiftGen', '~> 6.6.2' pod 'Kanna', '~> 5.2.2' diff --git a/Podfile.lock b/Podfile.lock index fd14828b9..24522c82e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -4,27 +4,23 @@ PODS: - Sourcery/CLI-Only (= 1.9.2) - Sourcery/CLI-Only (1.9.2) - SwiftGen (6.6.2) - - XLPagerTabStrip (9.0.0) DEPENDENCIES: - Kanna (~> 5.2.2) - Sourcery (~> 1.9) - SwiftGen (~> 6.6.2) - - XLPagerTabStrip (~> 9.0.0) SPEC REPOS: trunk: - Kanna - Sourcery - SwiftGen - - XLPagerTabStrip SPEC CHECKSUMS: Kanna: 01cfbddc127f5ff0963692f285fcbc8a9d62d234 Sourcery: 179539341c2261068528cd15a31837b7238fd901 SwiftGen: 1366a7f71aeef49954ca5a63ba4bef6b0f24138c - XLPagerTabStrip: 61c57fd61f611ee5f01ff1495ad6fbee8bf496c5 -PODFILE CHECKSUM: 8c962b3cbb4c225f1e57fb2e4ca03d1f22c45e5e +PODFILE CHECKSUM: 597c21d7aa08efec996048577c3c4fbeffbb6305 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/README.md b/README.md index f28caf8bd..18756bc6f 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ [![CI](https://github.com/mastodon/mastodon-ios/actions/workflows/main.yml/badge.svg)](https://github.com/mastodon/mastodon-ios/actions/workflows/main.yml) [![Crowdin](https://badges.crowdin.net/mastodon-for-ios/localized.svg)](https://crowdin.com/project/mastodon-for-ios) - Download on the App Store ## Introduction -This is the repository for the official iOS App for Mastodon. You can install it from the App Store now. You can build the app from source and file bug report here. +This is the repository for the official iOS App for Mastodon. You can install it from the App Store now. You can build the app from source and file bug report here. + +Read this blog post for this app to learn more. -Read this blog post for this app to learn more. > [Developing an official iOS app for Mastodon](https://blog.joinmastodon.org/2021/02/developing-an-official-ios-app-for-mastodon/) ## Getting Started @@ -17,6 +17,7 @@ Read this blog post for this app to learn more. - Read the setup guide [here](./Documentation/Setup.md) - About [contributing](./Documentation/CONTRIBUTING.md) - [Documentation folder](./Documentation/) +- [App Store Deployment](./Documentation/Deployment.md) ## Acknowledgments diff --git a/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift b/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift index bef484e39..1f118aeeb 100644 --- a/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift +++ b/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift @@ -54,10 +54,7 @@ struct FollowersCountEntry: TimelineEntry { struct FollowersCountWidget: Widget { private var availableFamilies: [WidgetFamily] { - if #available(iOS 16, *) { - return [.systemSmall, .accessoryRectangular, .accessoryCircular] - } - return [.systemSmall] + return [.systemSmall, .accessoryRectangular, .accessoryCircular] } var body: some WidgetConfiguration { diff --git a/WidgetExtension/Variants/FollowersCount/FollowersCountWidgetView.swift b/WidgetExtension/Variants/FollowersCount/FollowersCountWidgetView.swift index 2ad348c71..2c781b36e 100644 --- a/WidgetExtension/Variants/FollowersCount/FollowersCountWidgetView.swift +++ b/WidgetExtension/Variants/FollowersCount/FollowersCountWidgetView.swift @@ -148,9 +148,7 @@ struct FollowersCountWidgetView: View { private func viewForAccessoryCircular(_ account :FollowersEntryAccountable) -> some View { ZStack { - if #available(iOS 16, *) { - AccessoryWidgetBackground() - } + AccessoryWidgetBackground() VStack { Image("BrandIcon") diff --git a/WidgetExtension/Variants/Hashtag/HashtagWidget.swift b/WidgetExtension/Variants/Hashtag/HashtagWidget.swift index 86e9f485c..41e21da83 100644 --- a/WidgetExtension/Variants/Hashtag/HashtagWidget.swift +++ b/WidgetExtension/Variants/Hashtag/HashtagWidget.swift @@ -146,11 +146,7 @@ struct HashtagWidgetTimelineEntry: TimelineEntry { struct HashtagWidget: Widget { private var availableFamilies: [WidgetFamily] { - if #available(iOS 16, *) { - return [.systemMedium, .systemLarge, .accessoryRectangular] - } else { - return [.systemMedium, .systemLarge] - } + return [.systemMedium, .systemLarge, .accessoryRectangular] } var body: some WidgetConfiguration { diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 000000000..c55fca302 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,2 @@ +app_identifier("org.joinmastodon.app") +team_id("5Z4GVSS33P") diff --git a/fastlane/Deliverfile b/fastlane/Deliverfile new file mode 100644 index 000000000..5717b1eb3 --- /dev/null +++ b/fastlane/Deliverfile @@ -0,0 +1,3 @@ +force true +app_identifier "org.joinmastodon.app" +precheck_include_in_app_purchases false \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..b1defa95c --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,80 @@ +opt_out_usage +default_platform(:ios) + +$appName = "Mastodon" + +platform :ios do + before_all do |lane| + $bundle_id = "org.joinmastodon.app" + $all_bundle_ids = [ + $bundle_id, + "org.joinmastodon.app.MastodonIntent", + "org.joinmastodon.app.NotificationService", + "org.joinmastodon.app.ShareActionExtension", + "org.joinmastodon.app.OpenInActionExtension", + "org.joinmastodon.app.WidgetExtension" + ] + + lanes_for_building = [:deploy_appstore] + + if lanes_for_building.include?(lane) + app_store_connect_api_key( + key_id: ENV["ITC_KEY_ID"], + issuer_id: ENV["ITC_ISSUER_ID"], + key_content: ENV["ITC_KEY"], + duration: 1200, + in_house: false + ) + ensure_git_status_clean + $version_number = get_version_number_from_xcodeproj(target: $appName) + $build_number = get_build_number() + increment_build_number_in_xcodeproj( + xcodeproj: "#{$appName}.xcodeproj", + build_number: $build_number + ) + end + end + + desc "Update certificates" + lane :update_certificates do + match(type: "development", app_identifier: $all_bundle_ids, force_for_new_devices: true) + match(type: "appstore", app_identifier: $all_bundle_ids, force_for_new_devices: false) + end + + desc "Update devices" + lane :update_devices do + register_devices(devices_file: "./fastlane/devices.txt") + end + + lane :build_only do + xcodebuild( + clean: true, + scheme: "Mastodon", + workspace: "Mastodon.xcworkspace" + ) + end + + desc " Build and deploy the App to App Store Connect & TestFlight" + lane :deploy_appstore do + + if is_ci + create_keychain(name: "temp_keychain", password: "temp_123456", default_keychain: true, unlock: true, timeout: 3600, lock_when_sleeps: false) + match(type: "appstore", app_identifier: $all_bundle_ids, force_for_new_devices: true, readonly: true, keychain_name: "temp_keychain", keychain_password: "temp_123456") + else + match(type: "appstore", app_identifier: $all_bundle_ids, force_for_new_devices: true, readonly: false) + end + + gym(workspace: "#{$appName}.xcworkspace", + scheme: "#{$appName}", + clean: true, + export_method: "app-store", + export_xcargs: "-allowProvisioningUpdates") + deliver(app_identifier: $bundle_id, skip_screenshots: true, skip_metadata: true) + + sh("echo \"GITHUB_TAG_NAME=#{$version_number}-#{$build_number}\" >> $GITHUB_ENV") + end +end + +def get_build_number + sh("git rev-list --count HEAD").chomp +end diff --git a/fastlane/Matchfile b/fastlane/Matchfile new file mode 100644 index 000000000..8ba507988 --- /dev/null +++ b/fastlane/Matchfile @@ -0,0 +1,14 @@ +git_url("git@github.com:mastodon/mastodon-ios-match.git") +storage_mode("git") +git_branch("main") +type("appstore") # The default type, can be: appstore, adhoc, enterprise or development + +app_identifier([ + "org.joinmastodon.app", + "org.joinmastodon.app.MastodonIntent", + "org.joinmastodon.app.NotificationService", + "org.joinmastodon.app.ShareActionExtension", + "org.joinmastodon.app.OpenInActionExtension", + "org.joinmastodon.app.WidgetExtension" +]) +# username("fastlane@joinmastodon.org") diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 000000000..87578f80a --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-versioning' diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 000000000..87c6dfe1a --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,56 @@ +fastlane documentation +---- + +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +```sh +xcode-select --install +``` + +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) + +# Available Actions + +## iOS + +### ios update_certificates + +```sh +[bundle exec] fastlane ios update_certificates +``` + +Update certificates + +### ios update_devices + +```sh +[bundle exec] fastlane ios update_devices +``` + +Update devices + +### ios build_only + +```sh +[bundle exec] fastlane ios build_only +``` + + + +### ios deploy_appstore + +```sh +[bundle exec] fastlane ios deploy_appstore +``` + + Build and deploy the App to App Store Connect & TestFlight + +---- + +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/fastlane/devices.txt.example b/fastlane/devices.txt.example new file mode 100644 index 000000000..16e1b77b9 --- /dev/null +++ b/fastlane/devices.txt.example @@ -0,0 +1,2 @@ +Device ID Device Name Device Platform +00000000-0000000000000000 Your-Device-Name ios \ No newline at end of file