From a17125d4b4a251bf16277be42ba126aca9209123 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 25 Jul 2024 00:07:47 +0200 Subject: [PATCH 01/38] Update quicksy workflow to match current stable workflow --- .github/workflows/quicksy.build-push.yml | 16 +++++++++++----- .github/workflows/stable.build-push.yml | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/quicksy.build-push.yml b/.github/workflows/quicksy.build-push.yml index d8562007b..6f3fd18f4 100644 --- a/.github/workflows/quicksy.build-push.yml +++ b/.github/workflows/quicksy.build-push.yml @@ -120,15 +120,21 @@ jobs: env: CHANGELOG: ${{ steps.releasenotes.outputs.notes_ios }} run: | - path="$(mktemp -d)" - echo -n "$CHANGELOG" > "$path/release_notes.txt" - echo "path=$path" | tee /dev/stderr >> "$GITHUB_OUTPUT" + path_ios="$(mktemp -d)" + cp -av ./appstore_metadata/* "$path_ios" + echo -n "$(date +%Y) Thilo Molitor" > "$path_ios/copyright.txt" + for dir in */; do + if [[ -d "$dir" && "$dir" == *-* ]]; then + echo -n "$CHANGELOG_IOS" > "$path_ios/${dir%/}/release_notes.txt" + fi + done + echo "path_ios=$path_ios" | tee /dev/stderr >> "$GITHUB_OUTPUT" - name: Publish ios to appstore connect #run: xcrun altool --upload-app --file ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y --team-id S8D843U34Y -u $(cat /Users/ci/apple_connect_upload_mail.txt) -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" env: - DELIVER_METADATA_PATH: ${{ steps.metadata.outputs.path }} + DELIVER_METADATA_PATH: ${{ steps.metadata.outputs.path_ios }} run: | - fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" reject_if_possible:true submit_for_review:false automatic_release:false skip_metadata: true skip_screenshots: true + fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true - name: Remove fastlane metadata directory run: | rm -rf "${{ steps.metadata.outputs.path }}" diff --git a/.github/workflows/stable.build-push.yml b/.github/workflows/stable.build-push.yml index 4f5a3c68d..42f1248eb 100644 --- a/.github/workflows/stable.build-push.yml +++ b/.github/workflows/stable.build-push.yml @@ -155,7 +155,7 @@ jobs: fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true - name: Notarize catalyst run: xcrun notarytool submit ./Monal/build/app/Monal.zip --wait --team-id S8D843U34Y --key "/Users/ci/appstoreconnect/apiKey.p8" --key-id "$(cat /Users/ci/appstoreconnect/apiKeyId.txt)" --issuer "$(cat /Users/ci/appstoreconnect/apiIssuerId.txt)" - - name: staple + - name: Staple notarisation run: | cd Monal/build/app/tar_release/ xcrun stapler staple "$APP_DIR" From 866e7ed3a0f09a84ec70e214ee6f6aac4bfdb64b Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 25 Jul 2024 00:33:33 +0200 Subject: [PATCH 02/38] Add fastlane platform to stable release workflows --- .github/workflows/quicksy.build-push.yml | 2 +- .github/workflows/stable.build-push.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/quicksy.build-push.yml b/.github/workflows/quicksy.build-push.yml index 6f3fd18f4..b2b60451d 100644 --- a/.github/workflows/quicksy.build-push.yml +++ b/.github/workflows/quicksy.build-push.yml @@ -134,7 +134,7 @@ jobs: env: DELIVER_METADATA_PATH: ${{ steps.metadata.outputs.path_ios }} run: | - fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true + fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" platform:ios reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true - name: Remove fastlane metadata directory run: | rm -rf "${{ steps.metadata.outputs.path }}" diff --git a/.github/workflows/stable.build-push.yml b/.github/workflows/stable.build-push.yml index 42f1248eb..03093a738 100644 --- a/.github/workflows/stable.build-push.yml +++ b/.github/workflows/stable.build-push.yml @@ -152,7 +152,7 @@ jobs: env: DELIVER_METADATA_PATH: ${{ steps.metadata.outputs.path_ios }} run: | - fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true + fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" app_version:"${{ steps.releasenotes.outputs.version }}" platform:ios reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true - name: Notarize catalyst run: xcrun notarytool submit ./Monal/build/app/Monal.zip --wait --team-id S8D843U34Y --key "/Users/ci/appstoreconnect/apiKey.p8" --key-id "$(cat /Users/ci/appstoreconnect/apiKeyId.txt)" --issuer "$(cat /Users/ci/appstoreconnect/apiIssuerId.txt)" - name: Staple notarisation @@ -179,7 +179,7 @@ jobs: env: DELIVER_METADATA_PATH: ${{ steps.metadata.outputs.path_macos }} run: | - fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" pkg:"./Monal/build/app/Monal.pkg" app_version:"${{ steps.releasenotes.outputs.version }}" reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true + fastlane run upload_to_app_store api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" pkg:"./Monal/build/app/Monal.pkg" app_version:"${{ steps.releasenotes.outputs.version }}" platform:mac reject_if_possible:true submit_for_review:true automatic_release:true skip_metadata:false skip_screenshots:true precheck_include_in_app_purchases:false version_check_wait_retry_limit:10 force:true # - name: Update xmpp.org client list with new timestamp # run: ./scripts/push_xmpp.org.sh - name: Remove fastlane metadata directory From ca81c4606a4d3a66c67bc8d959a1009598909bae Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 25 Jul 2024 03:00:39 +0200 Subject: [PATCH 03/38] Properly extract release id in publishing workflow --- .github/workflows/publish-quicksy-release.yml | 51 +-------- .github/workflows/publish-stable-release.yml | 103 +++++++++--------- 2 files changed, 53 insertions(+), 101 deletions(-) diff --git a/.github/workflows/publish-quicksy-release.yml b/.github/workflows/publish-quicksy-release.yml index 590edbaad..9b673afe5 100644 --- a/.github/workflows/publish-quicksy-release.yml +++ b/.github/workflows/publish-quicksy-release.yml @@ -1,7 +1,7 @@ name: Publish Quicksy release on: repository_dispatch: - types: [distribution] + types: [quicksy_release] jobs: extractChangelog: runs-on: self-hosted @@ -28,52 +28,3 @@ jobs: touch "$OUTPUT_FILE" cat "$OUTPUT_FILE" >> "$GITHUB_OUTPUT" - # notifyMuc: - # name: Notify support MUC about new stable release - # runs-on: ubuntu-latest - # needs: [extractChangelog] - # steps: - # - name: Notify support MUC - # uses: monal-im/xmpp-notifier@master - # with: # Set the secrets as inputs - # jid: ${{ secrets.BOT_JID }} - # password: ${{ secrets.BOT_PASSWORD }} - # server_host: ${{ secrets.BOT_SERVER }} - # recipient: monal@chat.yax.im - # recipient_is_room: true - # bot_alias: "Monal Release Bot" - # message: | - # ${{ needs.extractChangelog.outputs.release-name }} was released: - # ${{ needs.extractChangelog.outputs.release-notes }} - # - # notifyMastodon: - # name: Post release info on mastodon - # runs-on: ubuntu-latest - # needs: [extractChangelog] - # steps: - # - name: Patch changelog length - # id: changelog - # env: - # NOTES: ${{ needs.extractChangelog.outputs.release-notes }} - # run: | - # if [ "${#NOTES}" -gt 400 ]; then - # NOTES="To see the complete list of bugfixes and improvements, check our releases page: https://github.com/monal-im/Monal/releases/tag/${{ needs.extractChangelog.outputs.release-tag }}" - # fi - # echo "notes<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - # echo "$NOTES" >> "$GITHUB_OUTPUT" - # echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - # - name: Post release info on mastodon - # id: toot - # uses: cbrgm/mastodon-github-action@v2.1.3 - # with: - # access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} - # url: ${{ secrets.MASTODON_URL }} - # - # message: "${{ needs.extractChangelog.outputs.release-name }} released.\n\n${{ steps.changelog.outputs.notes }}\n\n#Monal #quicksy #ios #macos #xmpp #im #chat #messaging" - # visibility: "public" - # language: "en" - # - name: Get toot information - # run: | - # echo "Toot ID: ${{ steps.toot.outputs.id }}" - # echo "Toot URL: ${{ steps.toot.outputs.url }}" - # echo "Scheduled at: ${{ steps.toot.outputs.scheduled_at }}" diff --git a/.github/workflows/publish-stable-release.yml b/.github/workflows/publish-stable-release.yml index 13daf4504..87f20ee9a 100644 --- a/.github/workflows/publish-stable-release.yml +++ b/.github/workflows/publish-stable-release.yml @@ -1,7 +1,7 @@ name: Publish release on: repository_dispatch: - types: [distribution] + types: [monal_release] jobs: extractChangelog: runs-on: self-hosted @@ -13,6 +13,7 @@ jobs: release-notes: ${{ steps.releasenotes.outputs.notes }} release-notes_ios: ${{ steps.releasenotes.outputs.notes_ios }} release-notes_macos: ${{ steps.releasenotes.outputs.notes_macos }} + release-id: ${{ steps.releasenotes.outputs.releaseID }} # create release only if the ios app made it to the appstore and ignore the macos appstore state if: github.event.client_payload.Platform == 'iOS' steps: @@ -36,60 +37,60 @@ jobs: steps: - name: Promote draft release to live release run: | - echo "ID: ${{ steps.releasenotes.outputs.releaseID }}" + echo "ID: ${{ needs.extractChangelog.outputs.release-id }}" curl -L \ -X PATCH \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.draftrelease.outputs.id }}" \ + "https://api.github.com/repos/${{ github.repository }}/releases/${{ needs.extractChangelog.outputs.release-id }}" \ -d '{"draft": false, "prerelease": false, "make_latest": true}' - notifyMuc: - name: Notify support MUC about new stable release - runs-on: ubuntu-latest - needs: [extractChangelog] - steps: - - name: Notify support MUC - uses: monal-im/xmpp-notifier@master - with: # Set the secrets as inputs - jid: ${{ secrets.BOT_JID }} - password: ${{ secrets.BOT_PASSWORD }} - server_host: ${{ secrets.BOT_SERVER }} - recipient: monal@chat.yax.im - recipient_is_room: true - bot_alias: "Monal Release Bot" - message: | - ${{ needs.extractChangelog.outputs.release-name }} was released: - ${{ needs.extractChangelog.outputs.release-notes }} - - notifyMastodon: - name: Post release info on mastodon - runs-on: ubuntu-latest - needs: [extractChangelog] - steps: - - name: Patch changelog length - id: changelog - env: - NOTES: ${{ needs.extractChangelog.outputs.release-notes }} - run: | - if [ "${#NOTES}" -gt 400 ]; then - NOTES="To see the complete list of bugfixes and improvements, check our releases page: https://github.com/monal-im/Monal/releases/tag/${{ needs.extractChangelog.outputs.release-tag }}" - fi - echo "notes<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - echo "$NOTES" >> "$GITHUB_OUTPUT" - echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - - name: Post release info on mastodon - id: toot - uses: cbrgm/mastodon-github-action@v2.1.3 - with: - access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} - url: ${{ secrets.MASTODON_URL }} - message: "${{ needs.extractChangelog.outputs.release-name }} released.\n\n${{ steps.changelog.outputs.notes }}\n\n#Monal #ios #macos #xmpp #im #chat #messaging" - visibility: "public" - language: "en" - - name: Get toot information - run: | - echo "Toot ID: ${{ steps.toot.outputs.id }}" - echo "Toot URL: ${{ steps.toot.outputs.url }}" - echo "Scheduled at: ${{ steps.toot.outputs.scheduled_at }}" + # notifyMuc: + # name: Notify support MUC about new stable release + # runs-on: ubuntu-latest + # needs: [extractChangelog] + # steps: + # - name: Notify support MUC + # uses: monal-im/xmpp-notifier@master + # with: # Set the secrets as inputs + # jid: ${{ secrets.BOT_JID }} + # password: ${{ secrets.BOT_PASSWORD }} + # server_host: ${{ secrets.BOT_SERVER }} + # recipient: monal@chat.yax.im + # recipient_is_room: true + # bot_alias: "Monal Release Bot" + # message: | + # ${{ needs.extractChangelog.outputs.release-name }} was released: + # ${{ needs.extractChangelog.outputs.release-notes }} + # + # notifyMastodon: + # name: Post release info on mastodon + # runs-on: ubuntu-latest + # needs: [extractChangelog] + # steps: + # - name: Patch changelog length + # id: changelog + # env: + # NOTES: ${{ needs.extractChangelog.outputs.release-notes }} + # run: | + # if [ "${#NOTES}" -gt 400 ]; then + # NOTES="To see the complete list of bugfixes and improvements, check our releases page: https://github.com/monal-im/Monal/releases/tag/${{ needs.extractChangelog.outputs.release-tag }}" + # fi + # echo "notes<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + # echo "$NOTES" >> "$GITHUB_OUTPUT" + # echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + # - name: Post release info on mastodon + # id: toot + # uses: cbrgm/mastodon-github-action@v2.1.3 + # with: + # access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} + # url: ${{ secrets.MASTODON_URL }} + # message: "${{ needs.extractChangelog.outputs.release-name }} released.\n\n${{ steps.changelog.outputs.notes }}\n\n#Monal #ios #macos #xmpp #im #chat #messaging" + # visibility: "public" + # language: "en" + # - name: Get toot information + # run: | + # echo "Toot ID: ${{ steps.toot.outputs.id }}" + # echo "Toot URL: ${{ steps.toot.outputs.url }}" + # echo "Scheduled at: ${{ steps.toot.outputs.scheduled_at }}" From c57034a22b6d90ba6f2b19ea429e1d7678ddce13 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 25 Jul 2024 03:20:46 +0200 Subject: [PATCH 04/38] Add mastodon and muc notifications back in --- .github/workflows/publish-stable-release.yml | 96 ++++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/publish-stable-release.yml b/.github/workflows/publish-stable-release.yml index 87f20ee9a..09abd9ef9 100644 --- a/.github/workflows/publish-stable-release.yml +++ b/.github/workflows/publish-stable-release.yml @@ -46,51 +46,51 @@ jobs: "https://api.github.com/repos/${{ github.repository }}/releases/${{ needs.extractChangelog.outputs.release-id }}" \ -d '{"draft": false, "prerelease": false, "make_latest": true}' - # notifyMuc: - # name: Notify support MUC about new stable release - # runs-on: ubuntu-latest - # needs: [extractChangelog] - # steps: - # - name: Notify support MUC - # uses: monal-im/xmpp-notifier@master - # with: # Set the secrets as inputs - # jid: ${{ secrets.BOT_JID }} - # password: ${{ secrets.BOT_PASSWORD }} - # server_host: ${{ secrets.BOT_SERVER }} - # recipient: monal@chat.yax.im - # recipient_is_room: true - # bot_alias: "Monal Release Bot" - # message: | - # ${{ needs.extractChangelog.outputs.release-name }} was released: - # ${{ needs.extractChangelog.outputs.release-notes }} - # - # notifyMastodon: - # name: Post release info on mastodon - # runs-on: ubuntu-latest - # needs: [extractChangelog] - # steps: - # - name: Patch changelog length - # id: changelog - # env: - # NOTES: ${{ needs.extractChangelog.outputs.release-notes }} - # run: | - # if [ "${#NOTES}" -gt 400 ]; then - # NOTES="To see the complete list of bugfixes and improvements, check our releases page: https://github.com/monal-im/Monal/releases/tag/${{ needs.extractChangelog.outputs.release-tag }}" - # fi - # echo "notes<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - # echo "$NOTES" >> "$GITHUB_OUTPUT" - # echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - # - name: Post release info on mastodon - # id: toot - # uses: cbrgm/mastodon-github-action@v2.1.3 - # with: - # access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} - # url: ${{ secrets.MASTODON_URL }} - # message: "${{ needs.extractChangelog.outputs.release-name }} released.\n\n${{ steps.changelog.outputs.notes }}\n\n#Monal #ios #macos #xmpp #im #chat #messaging" - # visibility: "public" - # language: "en" - # - name: Get toot information - # run: | - # echo "Toot ID: ${{ steps.toot.outputs.id }}" - # echo "Toot URL: ${{ steps.toot.outputs.url }}" - # echo "Scheduled at: ${{ steps.toot.outputs.scheduled_at }}" + notifyMuc: + name: Notify support MUC about new stable release + runs-on: ubuntu-latest + needs: [extractChangelog] + steps: + - name: Notify support MUC + uses: monal-im/xmpp-notifier@master + with: # Set the secrets as inputs + jid: ${{ secrets.BOT_JID }} + password: ${{ secrets.BOT_PASSWORD }} + server_host: ${{ secrets.BOT_SERVER }} + recipient: monal@chat.yax.im + recipient_is_room: true + bot_alias: "Monal Release Bot" + message: | + ${{ needs.extractChangelog.outputs.release-name }} was released: + ${{ needs.extractChangelog.outputs.release-notes }} + + notifyMastodon: + name: Post release info on mastodon + runs-on: ubuntu-latest + needs: [extractChangelog] + steps: + - name: Patch changelog length + id: changelog + env: + NOTES: ${{ needs.extractChangelog.outputs.release-notes }} + run: | + if [ "${#NOTES}" -gt 400 ]; then + NOTES="To see the complete list of bugfixes and improvements, check our releases page: https://github.com/monal-im/Monal/releases/tag/${{ needs.extractChangelog.outputs.release-tag }}" + fi + echo "notes<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "$NOTES" >> "$GITHUB_OUTPUT" + echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + - name: Post release info on mastodon + id: toot + uses: cbrgm/mastodon-github-action@v2.1.3 + with: + access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} + url: ${{ secrets.MASTODON_URL }} + message: "${{ needs.extractChangelog.outputs.release-name }} released.\n\n${{ steps.changelog.outputs.notes }}\n\n#Monal #ios #macos #xmpp #im #chat #messaging" + visibility: "public" + language: "en" + - name: Get toot information + run: | + echo "Toot ID: ${{ steps.toot.outputs.id }}" + echo "Toot URL: ${{ steps.toot.outputs.url }}" + echo "Scheduled at: ${{ steps.toot.outputs.scheduled_at }}" From b301b01a3485f03b24ed374d5b4e3b62cdc0e54f Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 05:01:37 +0200 Subject: [PATCH 05/38] --- BEGIN BACKPORTS --- backports to 6.4.1 From 410200bbf5c723bd9540de96d42132a00d2f5dd8 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 04:49:20 +0200 Subject: [PATCH 06/38] Don't vacuum on background task start, fixes crash Sometimes a background task is started while the app is already running (in background or in foreground). Doing a db vacuum will fail in this case because we don't have exclusive db access. Since we use secure_delete everywhere and even do a vacuum on db upgrades, there is no need to vacuum in a background task, too. --- Monal/Classes/DataLayer.m | 4 ++-- Monal/Classes/IPC.m | 25 +++++++++++++++++++++---- Monal/Classes/MonalAppDelegate.m | 3 --- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Monal/Classes/DataLayer.m b/Monal/Classes/DataLayer.m index c8d8d80f4..22e80889e 100644 --- a/Monal/Classes/DataLayer.m +++ b/Monal/Classes/DataLayer.m @@ -98,11 +98,11 @@ -(id) init //set wal mode (this setting is permanent): https://www.sqlite.org/pragma.html#pragma_journal_mode //this is a special case because it can not be done while in a transaction!!! [self.db enableWAL]; - + [self.db executeNonQuery:@"PRAGMA secure_delete=on;"]; + //needed for sqlite >= 3.26.0 (see https://sqlite.org/lang_altertable.html point 2) [self.db executeNonQuery:@"PRAGMA legacy_alter_table=on;"]; [self.db executeNonQuery:@"PRAGMA foreign_keys=off;"]; - [self.db executeNonQuery:@"PRAGMA secure_delete=on;"]; //do db upgrades and vacuum db afterwards if([DataLayerMigrations migrateDB:self.db withDataLayer:self]) diff --git a/Monal/Classes/IPC.m b/Monal/Classes/IPC.m index 99843391f..5cebf8b3e 100755 --- a/Monal/Classes/IPC.m +++ b/Monal/Classes/IPC.m @@ -136,7 +136,7 @@ -(id) initWithProcessName:(NSString*) processName _serverThreadCondition = [NSCondition new]; static dispatch_once_t once; - static const int VERSION = 2; + static const int VERSION = 3; dispatch_once(&once, ^{ BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:_dbFile]; //first command creates initial database if file does not exist @@ -144,7 +144,13 @@ -(id) initWithProcessName:(NSString*) processName //this will create the database file and open the database because it is the first MLSQlite call done for this file //turning on WAL mode has to be done *outside* of any transactions [self.db enableWAL]; - [self.db voidWriteTransaction:^{ + [self.db executeNonQuery:@"PRAGMA secure_delete=on;"]; + + //needed for sqlite >= 3.26.0 (see https://sqlite.org/lang_altertable.html point 2) + [self.db executeNonQuery:@"PRAGMA legacy_alter_table=on;"]; + [self.db executeNonQuery:@"PRAGMA foreign_keys=off;"]; + + NSNumber* version = [self.db idWriteTransaction:^{ if(!fileExists) { [self.db executeNonQuery:@"CREATE TABLE ipc(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name VARCHAR(255), source VARCHAR(255), destination VARCHAR(255), data BLOB, timeout INTEGER NOT NULL DEFAULT 0);"]; @@ -156,9 +162,12 @@ -(id) initWithProcessName:(NSString*) processName NSNumber* version = (NSNumber*)[self.db executeScalar:@"SELECT version FROM versions WHERE name='db';"]; DDLogInfo(@"IPC db version: %@", version); if([version integerValue] < 2) - { [self.db executeNonQuery:@"ALTER TABLE ipc ADD COLUMN response_to INTEGER NOT NULL DEFAULT 0;"]; - } + + //do a vacuum and from now on do it on every db upgrade + if([version integerValue] < 3) + ; + //any upgrade done --> update version table and delete all old ipc messages if([version integerValue] < VERSION) { @@ -167,7 +176,15 @@ -(id) initWithProcessName:(NSString*) processName [self.db executeNonQuery:@"UPDATE versions SET version=? WHERE name='db';" andArguments:@[[NSNumber numberWithInt:VERSION]]]; DDLogInfo(@"IPC db upgraded to version: %d", VERSION); } + return version; }]; + if([version integerValue] < VERSION) + [self.db vacuum]; + + //turn foreign keys on again + //needed for sqlite >= 3.26.0 (see https://sqlite.org/lang_altertable.html point 2) + [self.db executeNonQuery:@"PRAGMA legacy_alter_table=off;"]; + [self.db executeNonQuery:@"PRAGMA foreign_keys=on;"]; }); //use a dedicated and very high priority thread to make sure this always runs diff --git a/Monal/Classes/MonalAppDelegate.m b/Monal/Classes/MonalAppDelegate.m index dc15a8d2a..2add71ae4 100644 --- a/Monal/Classes/MonalAppDelegate.m +++ b/Monal/Classes/MonalAppDelegate.m @@ -1619,9 +1619,6 @@ -(void) handleBackgroundProcessingTask:(BGTask*) task if(![[MLXMPPManager sharedInstance] hasConnectivity]) DDLogError(@"BGTASK has *no* connectivity? That's strange!"); - //we are a bg processing task potentially having minutes of background time --> vacuum database - [[DataLayer sharedInstance] vacuum]; - [self startBackgroundTimer:BGPROCESS_GRACEFUL_TIMEOUT]; @synchronized(self) { DDLogVerbose(@"Setting _shutdownPending to NO..."); From 609be9ca77ff5e42afa7f3be7ab24da2b8d46dd6 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 05:48:16 +0200 Subject: [PATCH 07/38] Add links to faq and considerations for server administrators The FAQ link is placed below the "submit a bugs" entry in the settings, while the "considerations for server administrators" link is placed below the server version in the server details. --- Monal/Classes/MLServerDetails.m | 50 +++++++++++++++++-- Monal/Classes/MLSettingsTableViewController.m | 14 ++++++ .../Base.lproj/Settings.storyboard | 1 + 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/Monal/Classes/MLServerDetails.m b/Monal/Classes/MLServerDetails.m index 0c7520303..9b50a2235 100644 --- a/Monal/Classes/MLServerDetails.m +++ b/Monal/Classes/MLServerDetails.m @@ -349,10 +349,14 @@ -(UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NS NSString* serverName = nilDefault(self.serverVersion.appName, NSLocalizedString(@"", @"server details")); NSString* serverVersion = nilDefault(self.serverVersion.appVersion, NSLocalizedString(@"", @"server details")); NSString* serverPlatform = self.serverVersion.platformOs != nil ? [NSString stringWithFormat:NSLocalizedString(@" running on %@", @"server details"), self.serverVersion.platformOs] : @""; + NSString* description = [NSString stringWithFormat:NSLocalizedString(@"version %@%@", @"server details"), serverVersion, serverPlatform]; + NSString* linkText = NSLocalizedString(@"Considerations for Server Administrators", @"server details"); + NSMutableAttributedString* attributedDescription = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n\n%@", description, linkText]]; + [attributedDescription addAttribute:NSLinkAttributeName value:@"https://github.com/monal-im/Monal/wiki/Considerations-for-XMPP-server-admins" range:NSMakeRange(description.length+2, linkText.length)]; dic = @{ @"Color": SERVER_DETAILS_COLOR_NONE, @"Title": serverName, - @"Description": [NSString stringWithFormat:NSLocalizedString(@"version %@%@", @"server details"), serverVersion, serverPlatform], + @"Description": attributedDescription, }; } } @@ -371,8 +375,25 @@ -(UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NS else if(indexPath.section == CB_SECTION) dic = [self.channelBindingTypes objectAtIndex:(NSUInteger)indexPath.row]; - cell.textLabel.text = nilExtractor([dic objectForKey:@"Title"]); - cell.detailTextLabel.text = nilExtractor([dic objectForKey:@"Description"]); + if([[dic objectForKey:@"Title"] isKindOfClass:NSClassFromString(@"NSMutableAttributedString")]) + { + cell.textLabel.attributedText = nilExtractor([dic objectForKey:@"Title"]); + UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]; + [cell.textLabel addGestureRecognizer:tapGesture]; + cell.textLabel.userInteractionEnabled = YES; + } + else + cell.textLabel.text = nilExtractor([dic objectForKey:@"Title"]); + + if([[dic objectForKey:@"Description"] isKindOfClass:NSClassFromString(@"NSMutableAttributedString")]) + { + cell.detailTextLabel.attributedText = nilExtractor([dic objectForKey:@"Description"]); + UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]; + [cell.detailTextLabel addGestureRecognizer:tapGesture]; + cell.detailTextLabel.userInteractionEnabled = YES; + } + else + cell.detailTextLabel.text = nilExtractor([dic objectForKey:@"Description"]); // Add background color to selected cells if([dic objectForKey:@"Color"]) @@ -409,6 +430,29 @@ -(UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NS return cell; } +-(void) handleTapOnLabel:(UITapGestureRecognizer*) recognizer +{ + UILabel* label = (UILabel*) recognizer.view; + CGPoint location = [recognizer locationInView:label]; + NSAttributedString* attributedText = label.attributedText; + + NSTextStorage* textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText]; + NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init]; + NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:label.bounds.size]; + textContainer.lineFragmentPadding = 0; + [layoutManager addTextContainer:textContainer]; + [textStorage addLayoutManager:layoutManager]; + + NSUInteger characterIndex = [layoutManager characterIndexForPoint:location inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nil]; + + if(characterIndex < attributedText.length) + { + NSString* url = [attributedText attribute:NSLinkAttributeName atIndex:characterIndex effectiveRange:nil]; + if(url) + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url] options:@{} completionHandler:nil]; + } +} + -(NSString*) tableView:(UITableView*) tableView titleForHeaderInSection:(NSInteger) section { if(section == SERVER_VERSION_SECTION) diff --git a/Monal/Classes/MLSettingsTableViewController.m b/Monal/Classes/MLSettingsTableViewController.m index 6fc4cb29a..9ed8c6a68 100644 --- a/Monal/Classes/MLSettingsTableViewController.m +++ b/Monal/Classes/MLSettingsTableViewController.m @@ -44,6 +44,7 @@ enum SettingsSupportRow { EmailRow, SubmitABugRow, + OpenFAQRow, SettingsSupportRowCnt }; @@ -194,6 +195,13 @@ -(void) prepareForSegue:(UIStoryboardSegue*) segue sender:(id) sender [web initViewWithUrl:[NSURL URLWithString:@"https://github.com/monal-im/Monal/issues"]]; } + else if([segue.identifier isEqualToString:@"showFAQ"]) + { + UINavigationController* nav = (UINavigationController*) segue.destinationViewController; + MLWebViewController* web = (MLWebViewController*) nav.topViewController; + + [web initViewWithUrl:[NSURL URLWithString:@"https://github.com/monal-im/Monal/wiki/FAQ---Frequently-Asked-Questions"]]; + } else if([segue.identifier isEqualToString:@"editXMPP"]) { XMPPEdit* editor = (XMPPEdit*) segue.destinationViewController.childViewControllers.firstObject; // segue.destinationViewController; @@ -263,6 +271,9 @@ -(UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NS case SubmitABugRow: [cell initTapCell:NSLocalizedString(@"Submit A Bug", @"")]; break; + case OpenFAQRow: + [cell initTapCell:NSLocalizedString(@"Frequently Asked Questions", @"")]; + break; default: unreachable(); } @@ -377,6 +388,9 @@ -(void)tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) case SubmitABugRow: [self performSegueWithIdentifier:@"showBug" sender:self]; break; + case OpenFAQRow: + [self performSegueWithIdentifier:@"showFAQ" sender:self]; + break; default: unreachable(); } diff --git a/Monal/localization/Base.lproj/Settings.storyboard b/Monal/localization/Base.lproj/Settings.storyboard index 62cdc5035..3e40196ec 100644 --- a/Monal/localization/Base.lproj/Settings.storyboard +++ b/Monal/localization/Base.lproj/Settings.storyboard @@ -238,6 +238,7 @@ + From d6e58c9be704c47c9b87a90e2a527996875cb1ce Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 02:36:19 +0200 Subject: [PATCH 08/38] Fix split view on phablets in landscape mode Show chat placeholder on phablets in settings and active chats --- Monal/Classes/ActiveChatsViewController.h | 1 + Monal/Classes/ActiveChatsViewController.m | 10 +++---- Monal/Classes/HelperTools.h | 2 -- Monal/Classes/HelperTools.m | 18 ----------- Monal/Classes/MLSettingsTableViewController.h | 1 + Monal/Classes/MLSettingsTableViewController.m | 13 ++++++++ Monal/Classes/MLSplitViewDelegate.m | 30 ++++++++++++------- .../Base.lproj/Settings.storyboard | 28 ++++++----------- 8 files changed, 48 insertions(+), 55 deletions(-) diff --git a/Monal/Classes/ActiveChatsViewController.h b/Monal/Classes/ActiveChatsViewController.h index 4a4aef98a..eddb6f885 100644 --- a/Monal/Classes/ActiveChatsViewController.h +++ b/Monal/Classes/ActiveChatsViewController.h @@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN -(void) presentCall:(MLCall*) call; -(void) presentChatWithContact:(MLContact* _Nullable) contact; -(void) presentChatWithContact:(MLContact* _Nullable) contact andCompletion:(monal_id_block_t _Nullable) completion; +-(void) presentSplitPlaceholder; -(void) refreshDisplay; -(void) showContacts; diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index 24771259a..c94e35991 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -383,7 +383,7 @@ -(void) viewWillAppear:(BOOL) animated DDLogDebug(@"active chats view will appear"); [super viewWillAppear:animated]; - [self openConversationPlaceholder:nil]; + [self presentSplitPlaceholder]; } -(void) viewWillDisappear:(BOOL) animated @@ -622,10 +622,10 @@ -(void) showWarningsIfNeeded }); } --(void) openConversationPlaceholder:(MLContact*) contact +-(void) presentSplitPlaceholder { // only show placeholder if we use a split view - if([HelperTools deviceUsesSplitView] == YES) + if(!self.splitViewController.collapsed) { DDLogVerbose(@"Presenting Chat Placeholder..."); UIViewController* detailsViewController = [[SwiftuiInterface new] makeViewWithName:@"ChatPlaceholder"]; @@ -767,13 +767,13 @@ -(void) presentChatWithContact:(MLContact*) contact andCompletion:(monal_id_bloc } // clear old chat before opening a new one (but not for splitView == YES) - if([HelperTools deviceUsesSplitView] == NO) + if(self.splitViewController.collapsed) [self.navigationController popViewControllerAnimated:NO]; // show placeholder if contact is nil, open chat otherwise if(contact == nil) { - [self openConversationPlaceholder:nil]; + [self presentSplitPlaceholder]; if(completion != nil) completion(@NO); return; diff --git a/Monal/Classes/HelperTools.h b/Monal/Classes/HelperTools.h index c927a03f1..128fe148d 100644 --- a/Monal/Classes/HelperTools.h +++ b/Monal/Classes/HelperTools.h @@ -191,8 +191,6 @@ void swizzle(Class c, SEL orig, SEL new); +(NSString*) appBuildVersionInfoFor:(MLVersionType) type; -+(BOOL) deviceUsesSplitView; - +(NSNumber*) currentTimestampInSeconds; +(NSNumber*) dateToNSNumberSeconds:(NSDate*) date; diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index 5d1b97b80..5d191be96 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -2729,24 +2729,6 @@ +(CIImage*) createQRCodeFromString:(NSString*) input return qrCode.outputImage; } -+(BOOL) deviceUsesSplitView -{ -#if TARGET_OS_MACCATALYST - return YES; -#else - switch ([[UIDevice currentDevice] userInterfaceIdiom]) { - case UIUserInterfaceIdiomPad: - return YES; - break; - case UIUserInterfaceIdiomPhone: - return NO; - default: - unreachable(); - return NO; - } -#endif -} - //taken from: https://stackoverflow.com/a/30932216/3528174 +(NSArray*) splitString:(NSString*) string withSeparator:(NSString*) separator andMaxSize:(NSUInteger)size { diff --git a/Monal/Classes/MLSettingsTableViewController.h b/Monal/Classes/MLSettingsTableViewController.h index 0135c73d6..719e50924 100644 --- a/Monal/Classes/MLSettingsTableViewController.h +++ b/Monal/Classes/MLSettingsTableViewController.h @@ -15,5 +15,6 @@ @interface MLSettingsTableViewController : AccountListController - (IBAction)close:(id) sender; +-(void) presentSplitPlaceholder; @end diff --git a/Monal/Classes/MLSettingsTableViewController.m b/Monal/Classes/MLSettingsTableViewController.m index 9ed8c6a68..1179984f4 100644 --- a/Monal/Classes/MLSettingsTableViewController.m +++ b/Monal/Classes/MLSettingsTableViewController.m @@ -113,6 +113,8 @@ -(void) viewWillAppear:(BOOL)animated _tappedVersionInfo = 0; self.selected = nil; + + [self presentSplitPlaceholder]; } -(void) viewWillDisappear:(BOOL) animated @@ -121,6 +123,17 @@ -(void) viewWillDisappear:(BOOL) animated [((MonalAppDelegate*)UIApplication.sharedApplication.delegate).activeChats sheetDismissed]; } +-(void) presentSplitPlaceholder +{ + // only show placeholder if we use a split view + if(!self.splitViewController.collapsed) + { + DDLogVerbose(@"Presenting Settings Placeholder..."); + UIViewController* detailsViewController = [[SwiftuiInterface new] makeViewWithName:@"ChatPlaceholder"]; + [self showDetailViewController:detailsViewController sender:self]; + } +} + #pragma mark - key commands -(BOOL) canBecomeFirstResponder diff --git a/Monal/Classes/MLSplitViewDelegate.m b/Monal/Classes/MLSplitViewDelegate.m index eeef10f51..931f8c55f 100644 --- a/Monal/Classes/MLSplitViewDelegate.m +++ b/Monal/Classes/MLSplitViewDelegate.m @@ -7,24 +7,32 @@ // #import "MLSplitViewDelegate.h" -#import "chatViewController.h" -#import "XMPPEdit.h" +#import "ActiveChatsViewController.h" +#import "MLSettingsTableViewController.h" @implementation MLSplitViewDelegate #pragma mark - Split view -- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController { - - return YES; +-(BOOL) splitViewController:(UISplitViewController*) splitViewController collapseSecondaryViewController:(UIViewController*) secondaryViewController ontoPrimaryViewController:(UIViewController*) primaryViewController +{ + //return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded. + return YES; +} + +-(void) splitViewControllerDidExpand:(UISplitViewController*) splitViewController +{ + UIViewController* primaryController = ((UINavigationController*)splitViewController.viewControllers[0]).viewControllers[0]; + UIViewController* secondaryController = nil; + if([splitViewController.viewControllers count] > 1) + secondaryController = splitViewController.viewControllers[1]; + + if([primaryController isKindOfClass:NSClassFromString(@"ActiveChatsViewController")] && [secondaryController isKindOfClass:NSClassFromString(@"MLPlaceholderViewController")]) + [(ActiveChatsViewController*)primaryController presentSplitPlaceholder]; -// if ([secondaryViewController isKindOfClass:[UINavigationController class]] &&( [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[chatViewController class]] || [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[XMPPEdit class]]) ){ -// // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded. -// return YES; -// } else { -// return NO; -// } + if([primaryController isKindOfClass:NSClassFromString(@"MLSettingsTableViewController")] && [secondaryController isKindOfClass:NSClassFromString(@"MLPlaceholderViewController")]) + [(MLSettingsTableViewController*)primaryController presentSplitPlaceholder]; } @end diff --git a/Monal/localization/Base.lproj/Settings.storyboard b/Monal/localization/Base.lproj/Settings.storyboard index 3e40196ec..aa0379e60 100644 --- a/Monal/localization/Base.lproj/Settings.storyboard +++ b/Monal/localization/Base.lproj/Settings.storyboard @@ -424,6 +424,13 @@ + + + + + + + @@ -432,8 +439,7 @@ - - + @@ -543,22 +549,6 @@ - - - - - - - - - - - - - - - - @@ -834,7 +824,7 @@ - + From b6390a567f2a8aa5a7d6a564ab9d9ad86681605e Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 18:29:34 +0200 Subject: [PATCH 09/38] Emphasize digital sovereignty in onboarding texts --- Monal/Classes/BoardingCards.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Monal/Classes/BoardingCards.swift b/Monal/Classes/BoardingCards.swift index b3186e2b9..cc23d6b09 100644 --- a/Monal/Classes/BoardingCards.swift +++ b/Monal/Classes/BoardingCards.swift @@ -169,7 +169,7 @@ func createOnboardingView(delegate: SheetDismisserProtocol) -> some View { description: Text("Become part of a worldwide decentralized chat network!"), imageName: "hand.wave", articleText: Text(""" - Modern iOS and macOS XMPP chat client.\n\nXMPP is a federated network: Just like email, you can register your account on many servers and still talk to anyone, even if they signed up on a different server. + Modern iOS and macOS XMPP chat client.\n\nXMPP is a federated network: Just like email, you can register your account on many servers and still talk to anyone, even if they signed up on a different server.\n\nUsing Monal instead of a centralized chat app therefore increases your digital sovereignty. """), customView: nil, nextText: nil @@ -180,7 +180,7 @@ func createOnboardingView(delegate: SheetDismisserProtocol) -> some View { imageName: "sparkles", articleText: Text(""" 🛜 Decentralized Network : - Leverages the decentralized nature of XMPP, avoiding central servers. + Leverages the decentralized nature of XMPP, avoiding central servers and increasing your digital sovereignty. 🌐 Data privacy : We do not sell or track information for external parties (nor for anyone else). From bddff54904e3f04328479a4599d2b12af4d68a25 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 23:46:14 +0200 Subject: [PATCH 10/38] Centralize filename sanitization --- Monal/Classes/HelperTools.h | 2 +- Monal/Classes/HelperTools.m | 26 ++++++++++++-------------- Monal/Classes/MLHandler.m | 6 +----- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Monal/Classes/HelperTools.h b/Monal/Classes/HelperTools.h index 128fe148d..636809c64 100644 --- a/Monal/Classes/HelperTools.h +++ b/Monal/Classes/HelperTools.h @@ -182,8 +182,8 @@ void swizzle(Class c, SEL orig, SEL new); +(UIView*) MLCustomViewHeaderWithTitle:(NSString*) title; +(CIImage*) createQRCodeFromString:(NSString*) input; - +(AnyPromise*) waitAtLeastSeconds:(NSTimeInterval) seconds forPromise:(AnyPromise*) promise; ++(NSString*) sanitizeFilePath:(const char* const) file; //don't use these four directly, but via createTimer() makro +(MLDelayableTimer*) startDelayableQueuedTimer:(double) timeout withHandler:(monal_void_block_t) handler andCancelHandler:(monal_void_block_t _Nullable) cancelHandler andFile:(char*) file andLine:(int) line andFunc:(char*) func onQueue:(dispatch_queue_t _Nullable) queue; diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index 5d191be96..fefd5649a 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -338,10 +338,7 @@ +(void) installExceptionHandler +(void) __attribute__((noreturn)) MLAssertWithText:(NSString*) text andUserData:(id) userInfo andFile:(const char* const) file andLine:(int) line andFunc:(const char* const) func { - NSString* fileStr = [NSString stringWithFormat:@"%s", file]; - NSArray* filePathComponents = [fileStr pathComponents]; - if([filePathComponents count]>1) - fileStr = [NSString stringWithFormat:@"%@/%@", filePathComponents[[filePathComponents count]-2], filePathComponents[[filePathComponents count]-1]]; + NSString* fileStr = [self sanitizeFilePath:file]; DDLogError(@"Assertion triggered at %@:%d in %s", fileStr, line, func); @throw [NSException exceptionWithName:[NSString stringWithFormat:@"MLAssert triggered at %@:%d in %s with reason '%@' and userInfo: %@", fileStr, line, func, text, userInfo] reason:text userInfo:userInfo]; } @@ -396,10 +393,7 @@ +(void) postError:(NSString*) description withNode:(XMPPStanza* _Nullable) node +(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable) node andAccount:(xmpp* _Nullable) account andFile:(char*) file andLine:(int) line andFunc:(char*) func { - NSString* fileStr = [NSString stringWithFormat:@"%s", file]; - NSArray* filePathComponents = [fileStr pathComponents]; - if([filePathComponents count]>1) - fileStr = [NSString stringWithFormat:@"%@/%@", filePathComponents[[filePathComponents count]-2], filePathComponents[[filePathComponents count]-1]]; + NSString* fileStr = [self sanitizeFilePath:file]; NSString* message = description; if(node) message = [self extractXMPPError:node withDescription:description]; @@ -2269,16 +2263,20 @@ +(NSString*) generateDateTimeString:(NSDate*) datetime return [rfc3339DateFormatter stringFromDate:datetime]; } -//don't use this directly, but via createDelayableTimer() makros -+(MLDelayableTimer*) startDelayableQueuedTimer:(double) timeout withHandler:(monal_void_block_t) handler andCancelHandler:(monal_void_block_t _Nullable) cancelHandler andFile:(char*) file andLine:(int) line andFunc:(char*) func onQueue:(dispatch_queue_t _Nullable) queue ++(NSString*) sanitizeFilePath:(const char* const) file { - if(queue == nil) - queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - NSString* fileStr = [NSString stringWithFormat:@"%s", file]; NSArray* filePathComponents = [fileStr pathComponents]; if([filePathComponents count]>1) fileStr = [NSString stringWithFormat:@"%@/%@", filePathComponents[[filePathComponents count]-2], filePathComponents[[filePathComponents count]-1]]; + return fileStr; +} + +//don't use this directly, but via createDelayableTimer() makros ++(MLDelayableTimer*) startDelayableQueuedTimer:(double) timeout withHandler:(monal_void_block_t) handler andCancelHandler:(monal_void_block_t _Nullable) cancelHandler andFile:(char*) file andLine:(int) line andFunc:(char*) func onQueue:(dispatch_queue_t _Nullable) queue +{ + if(queue == nil) + queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); MLDelayableTimer* timer = [[MLDelayableTimer alloc] initWithHandler:^(MLDelayableTimer* timer){ if(handler) @@ -2292,7 +2290,7 @@ +(MLDelayableTimer*) startDelayableQueuedTimer:(double) timeout withHandler:(mon DDLogDebug(@"calling cancel block for timer: %@", timer); cancelHandler(); }); - } timeout:timeout tolerance:0.1 andDescription:[NSString stringWithFormat:@"created at %@:%d in %s", fileStr, line, func]]; + } timeout:timeout tolerance:0.1 andDescription:[NSString stringWithFormat:@"created at %@:%d in %s", [self sanitizeFilePath:file], line, func]]; if(timeout < 0.001) { diff --git a/Monal/Classes/MLHandler.m b/Monal/Classes/MLHandler.m index 2a55fc620..fd18d2c92 100644 --- a/Monal/Classes/MLHandler.m +++ b/Monal/Classes/MLHandler.m @@ -204,11 +204,7 @@ -(SEL) handlerNameToSelector:(NSString*) handlerName +(void) throwDynamicExceptionForType:(NSString*) type andVar:(NSString*) varName andUserData:(id) userInfo andFile:(char*) file andLine:(int) line andFunc:(char*) func { - NSString* fileStr = [NSString stringWithFormat:@"%s", file]; - NSArray* filePathComponents = [fileStr pathComponents]; - if([filePathComponents count]>1) - fileStr = [NSString stringWithFormat:@"%@/%@", filePathComponents[[filePathComponents count]-2], filePathComponents[[filePathComponents count]-1]]; - NSString* text = [NSString stringWithFormat:@"Dynamic unpacking exception triggered for '%@' var '%@' at %@:%d in %s", type, varName, fileStr, line, func]; + NSString* text = [NSString stringWithFormat:@"Dynamic unpacking exception triggered for '%@' var '%@' at %@:%d in %s", type, varName, [HelperTools sanitizeFilePath:file], line, func]; DDLogError(@"%@", text); @throw [NSException exceptionWithName:text reason:text userInfo:userInfo]; } From 417d4cd7a6e24dfbe64046988c9ab5790973f644 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 01:28:01 +0200 Subject: [PATCH 11/38] Make delayable timer unreachables mere alpha-only error notifications --- Monal/Classes/HelperTools.m | 2 +- Monal/Classes/MLDelayableTimer.m | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index fefd5649a..a5ca0221f 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -398,7 +398,7 @@ +(void) showErrorOnAlpha:(NSString*) description withNode:(XMPPStanza* _Nullable if(node) message = [self extractXMPPError:node withDescription:description]; #ifdef IS_ALPHA - DDLogError(@"Notifying alpha user about error at %@:%d in %s: %@", fileStr, line, func, message); + DDLogError(@"Notifying alpha user about error on account %@ at %@:%d in %s: %@", account, fileStr, line, func, message); if(account != nil) [[MLNotificationQueue currentQueue] postNotificationName:kXMPPError object:account userInfo:@{@"message": message, @"isSevere":@YES}]; else diff --git a/Monal/Classes/MLDelayableTimer.m b/Monal/Classes/MLDelayableTimer.m index cb627dbea..3658412e8 100644 --- a/Monal/Classes/MLDelayableTimer.m +++ b/Monal/Classes/MLDelayableTimer.m @@ -48,7 +48,7 @@ -(void) start @synchronized(self) { if(!_wrappedTimer.valid) { - unreachable(@"Could not start already fired timer!", @{@"timer": self}); + showErrorOnAlpha(nil, @"Could not start already fired timer: %@", self); return; } DDLogDebug(@"Starting timer: %@", self); @@ -61,7 +61,7 @@ -(void) trigger @synchronized(self) { if(!_wrappedTimer.valid) { - unreachable(@"Could not trigger already fired timer!", @{@"timer": self}); + showErrorOnAlpha(nil, @"Could not trigger already fired timer: %@", self); return; } DDLogDebug(@"Triggering timer: %@", self); @@ -116,7 +116,7 @@ -(void) invalidate @synchronized(self) { if(!_wrappedTimer.valid) { - unreachable(@"Could not invalidate already invalid timer!", @{@"timer": self}); + DDLogWarn(@"Could not invalidate already invalid timer: %@", self); return; } //DDLogVerbose(@"Invalidating timer: %@", self); From db989ad22405e88e2c514f9ace5229109fe208db Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sun, 28 Jul 2024 22:25:46 +0200 Subject: [PATCH 12/38] Improve onboarding layout --- Monal/Classes/BoardingCards.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Monal/Classes/BoardingCards.swift b/Monal/Classes/BoardingCards.swift index cc23d6b09..35edf93fc 100644 --- a/Monal/Classes/BoardingCards.swift +++ b/Monal/Classes/BoardingCards.swift @@ -50,6 +50,9 @@ struct OnboardingView: View { .foregroundColor(.blue) .padding(10) } + } else { + //make sure the space the "back" label will take, is already reserved to not have "jumps" when pressing next + Text("").padding(10) } HStack { From 235827372b8e9cd1a76d8faeffca00d308528632 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 07:33:34 +0200 Subject: [PATCH 13/38] Add new block types --- Monal/Classes/MLConstants.h | 6 ++++++ Monal/Classes/SwiftHelpers.swift | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Monal/Classes/MLConstants.h b/Monal/Classes/MLConstants.h index f512d23fb..2af2b6e0e 100644 --- a/Monal/Classes/MLConstants.h +++ b/Monal/Classes/MLConstants.h @@ -76,6 +76,8 @@ typedef void (^contactCompletion)(MLContact* _Nonnull selectedContact) NS_SWIFT_ typedef void (^accountCompletion)(NSInteger accountRow) NS_SWIFT_UNAVAILABLE("To be redefined in swift."); typedef void (^monal_void_block_t)(void) NS_SWIFT_UNAVAILABLE("To be redefined in swift."); typedef void (^monal_id_block_t)(id _Nonnull) NS_SWIFT_UNAVAILABLE("To be redefined in swift."); +typedef id _Nullable (^monal_id_returning_block_t)(id _Nonnull) NS_SWIFT_UNAVAILABLE("To be redefined in swift."); +typedef id _Nullable (^monal_id_returning_id_block_t)(id _Nonnull) NS_SWIFT_UNAVAILABLE("To be redefined in swift."); typedef void (^monal_upload_completion_t)(NSString* _Nullable url, NSString* _Nullable mimeType, NSNumber* _Nullable size, NSError* _Nullable error) NS_SWIFT_UNAVAILABLE("To be redefined in swift."); typedef NS_ENUM(NSUInteger, MLAudioState) { @@ -89,6 +91,10 @@ typedef NS_ENUM(NSUInteger, MLAudioState) { #define nilWrapper(var) (var == nil ? (id)[NSNull null] : (id)var) #define nilExtractor(var) ((id)var == [NSNull null] ? nil : var) #define nilDefault(var, def) (var == nil || (id)var == [NSNull null] ? def : var) +#define nilDefaultEnum(var, def) (((NSNumber*)nilDefault(var, def)).integerValue) +#define nilDefaultBool(var, def) (((NSNumber*)nilDefault(var, def)).boolValue) +#define nilDefaultInt(var, def) (((NSNumber*)nilDefault(var, def)).intValue) +#define nilDefaultDouble(var, def) (((NSNumber*)nilDefault(var, def)).doubleValue) #define emptyDefault(var, eq, def) (var == nil || (id)var == [NSNull null] || [var isEqual:eq] ? def : var) #define updateIfIdNotEqual(a, b) if(a != b && ![a isEqual:b]) a = b #define updateIfPrimitiveNotEqual(a, b) if(a != b) a = b diff --git a/Monal/Classes/SwiftHelpers.swift b/Monal/Classes/SwiftHelpers.swift index bd1e974f7..7e6a85c64 100644 --- a/Monal/Classes/SwiftHelpers.swift +++ b/Monal/Classes/SwiftHelpers.swift @@ -29,9 +29,11 @@ let LONG_PING = HelperTools.getObjcDefinedValue(.LONG_PING) let MUC_PING = HelperTools.getObjcDefinedValue(.MUC_PING) let BGFETCH_DEFAULT_INTERVAL = HelperTools.getObjcDefinedValue(.BGFETCH_DEFAULT_INTERVAL) +public typealias monal_timer_block_t = @convention(block) (MLDelayableTimer?) -> Void; public typealias monal_void_block_t = @convention(block) () -> Void; public typealias monal_id_block_t = @convention(block) (AnyObject?) -> Void; -public typealias monal_timer_block_t = @convention(block) (MLDelayableTimer?) -> Void; +public typealias monal_id_returning_block_t = @convention(block) () -> AnyObject?; +public typealias monal_id_returning_id_block_t = @convention(block) (AnyObject?) -> AnyObject?; //see https://stackoverflow.com/a/40629365/3528174 extension String: Error {} From dd6435740fedf6a9ccf8f927ab49ea76b04f4738 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 07:34:02 +0200 Subject: [PATCH 14/38] Add pubsub workaround for ejabberd <= 23.01 --- Monal/Classes/MLPubSub.m | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Monal/Classes/MLPubSub.m b/Monal/Classes/MLPubSub.m index ff554549d..52b647ebb 100644 --- a/Monal/Classes/MLPubSub.m +++ b/Monal/Classes/MLPubSub.m @@ -912,6 +912,26 @@ -(NSDictionary*) copyDefaultNodeOptions:(NSDictionary*) defaultOptions forConfig $$instance_handler(handlePublishResult, account.pubsub, $$ID(xmpp*, account), $$ID(XMPPIQ*, iqNode), $$ID(MLXMLNode*, item), $$ID(NSString*, node), $$ID(NSDictionary*, configOptions), $_HANDLER(handler), $$BOOL(is_retry)) if([iqNode check:@"/"]) { + //NOTE: workaround for old ejabberd versions < 23.10 only supporting two special settings as preconditions + if([@"http://www.process-one.net/en/ejabberd/" isEqualToString:account.connectionProperties.serverIdentity] && [configOptions count] > 0 && [iqNode check:@"error/{urn:ietf:params:xml:ns:xmpp-stanzas}resource-constraint"]) + { + DDLogError(@"ejabberd (< 23.10) workaround for old preconditions handling active for node: %@", node); + + //make sure we don't try all preconditions from configOptions again: only these two listed preconditions are safe to use with ejabberd + NSMutableDictionary* publishPreconditions = [NSMutableDictionary new]; + if(configOptions[@"pubsub#persist_items"]) + publishPreconditions[@"pubsub#persist_items"] = configOptions[@"pubsub#persist_items"]; + if(configOptions[@"pubsub#access_model"]) + publishPreconditions[@"pubsub#access_model"] = configOptions[@"pubsub#access_model"]; + + [self internalPublishItem:item onNode:node withConfigOptions:publishPreconditions andHandler:$newHandlerWithInvalidation(self, handleConfigureAfterPublish, handleConfigureAfterPublishInvalidation, + $ID(node), + $ID(configOptions), + $HANDLER(handler) + ) andIsRetry:NO]; + return; + } + //check if this node is already present and configured --> reconfigure it according to our access-model if(!is_retry && [iqNode check:@"error/{http://jabber.org/protocol/pubsub#errors}precondition-not-met"]) { From b0c8fed80d75023624c5f6d05ea7c77dd1c5c4e5 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Sat, 27 Jul 2024 20:54:42 +0200 Subject: [PATCH 15/38] Fix flickering of intro screens --- Monal/Classes/ActiveChatsViewController.h | 3 +- Monal/Classes/ActiveChatsViewController.m | 597 ++++++++++++++++------ Monal/Classes/BoardingCards.swift | 4 +- 3 files changed, 432 insertions(+), 172 deletions(-) diff --git a/Monal/Classes/ActiveChatsViewController.h b/Monal/Classes/ActiveChatsViewController.h index eddb6f885..71944fd32 100644 --- a/Monal/Classes/ActiveChatsViewController.h +++ b/Monal/Classes/ActiveChatsViewController.h @@ -26,7 +26,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) IBOutlet UIBarButtonItem* composeButton; @property (nonatomic, strong) chatViewController* currentChatViewController; @property (nonatomic, strong) UIActivityIndicatorView* spinner; -@property (nonatomic) BOOL enqueueGeneralSettings; -(void) showCallContactNotFoundAlert:(NSString*) jid; -(void) callContact:(MLContact*) contact withUIKitSender:(_Nullable id) sender; @@ -42,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN -(void) deleteConversation; -(void) showSettings; -(void) showGeneralSettings; --(void) showOnboarding; +-(void) prependGeneralSettings; -(void) showNotificationSettings; -(void) showDetails; -(void) showRegisterWithUsername:(NSString*) username onHost:(NSString*) host withToken:(NSString* _Nullable) token usingCompletion:(monal_id_block_t _Nullable) callback; diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index c94e35991..996f22c55 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -6,6 +6,8 @@ // // +#include "metamacros.h" + #import #import "ActiveChatsViewController.h" #import "DataLayer.h" @@ -25,6 +27,16 @@ #import "UIColor+Theme.h" #import +#define prependToViewQueue(firstArg, ...) metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))([self prependToViewQueue:firstArg withId:MLViewIDUnspecified andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__])(_prependToViewQueue(firstArg, __VA_ARGS__)) +#define _prependToViewQueue(ownId, block) [self prependToViewQueue:block withId:ownId andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__] +#define appendToViewQueue(firstArg, ...) metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))([self appendToViewQueue:firstArg withId:MLViewIDUnspecified andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__])(_appendToViewQueue(firstArg, __VA_ARGS__)) +#define _appendToViewQueue(ownId, block) [self prependToViewQueue:block withId:ownId andFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__] +#define appendingReplaceOnViewQueue(firstArg, secondArg, ...) metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))([self replaceIdOnViewQueue:firstArg withBlock:secondArg havingId:MLViewIDUnspecified andAppendOnUnknown:YES withFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__])(_appendingReplaceOnViewQueue(firstArg, secondArg, __VA_ARGS__)) +#define prependingReplaceOnViewQueue(firstArg, secondArg, ...) metamacro_if_eq(0, metamacro_argcount(__VA_ARGS__))([self replaceIdOnViewQueue:firstArg withBlock:secondArg havingId:MLViewIDUnspecified andAppendOnUnknown:NO withFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__])(_prependingReplaceOnViewQueue(firstArg, secondArg, __VA_ARGS__)) +#define _appendingReplaceOnViewQueue(replaceId, ownId, block) [self replaceIdOnViewQueue:replaceId withBlock:block havingId:ownId andAppendOnUnknown:YES withFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__] +#define _prependingReplaceOnViewQueue(replaceId, ownId, block) [self replaceIdOnViewQueue:replaceId withBlock:block havingId:ownId andAppendOnUnknown:NO withFile:(char*)__FILE__ andLine:__LINE__ andFunc:(char*)__func__] +typedef void (^view_queue_block_t)(PMKResolver _Nonnull); + @import QuartzCore.CATransaction; @interface DZNEmptyDataSetView @@ -43,6 +55,8 @@ @interface ActiveChatsViewController() { double _portraitTop; double _landscapeTop; BOOL _loginAlreadyAutodisplayed; + NSMutableArray* _blockQueue; + dispatch_semaphore_t _blockQueueSemaphore; } @property (atomic, strong) NSMutableArray* unpinnedContacts; @property (atomic, strong) NSMutableArray* pinnedContacts; @@ -56,6 +70,12 @@ @implementation ActiveChatsViewController activeChatsViewControllerSectionCnt }; +typedef NS_ENUM(NSUInteger, MLViewID) { + MLViewIDUnspecified, + MLViewIDRegisterView, + MLViewIDWelcomeLoginView, +}; + static NSMutableSet* _mamWarningDisplayed; static NSMutableSet* _smacksWarningDisplayed; static NSMutableSet* _pushWarningDisplayed; @@ -68,6 +88,154 @@ +(void) initialize _pushWarningDisplayed = [NSMutableSet new]; } +-(instancetype)initWithNibName:(NSString*) nibNameOrNil bundle:(NSBundle*) nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + [self commonInit]; + return self; +} + +-(instancetype) initWithStyle:(UITableViewStyle) style +{ + self = [super initWithStyle:style]; + [self commonInit]; + return self; +} + +-(instancetype) initWithCoder:(NSCoder*) coder +{ + self = [super initWithCoder:coder]; + [self commonInit]; + return self; +} + +-(void) commonInit +{ + _blockQueue = [NSMutableArray new]; + _blockQueueSemaphore = dispatch_semaphore_create(1); +} + +-(void) prependToViewQueue:(view_queue_block_t) block withId:(MLViewID) viewId andFile:(char*) file andLine:(int) line andFunc:(char*) func +{ + @synchronized(_blockQueue) { + DDLogDebug(@"Prepending block with id %lu defined in %s at %@:%d to queue...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + [_blockQueue insertObject:@{@"id":@(viewId), @"block":^(PMKResolver resolve) { + DDLogDebug(@"Calling block with id %lu defined in %s at %@:%d...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + block(resolve); + DDLogDebug(@"Block with id %lu defined in %s at %@:%d finished...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + }} atIndex:0]; + } + [self processViewQueue]; +} + +-(void) appendToViewQueue:(view_queue_block_t) block withId:(MLViewID) viewId andFile:(char*) file andLine:(int) line andFunc:(char*) func +{ + @synchronized(_blockQueue) { + DDLogDebug(@"Appending block with id %lu defined in %s at %@:%d to queue...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + [_blockQueue addObject:@{@"id":@(viewId), @"block":^(PMKResolver resolve) { + DDLogDebug(@"Calling block with id %lu defined in %s at %@:%d...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + block(resolve); + DDLogDebug(@"Block with id %lu defined in %s at %@:%d finished...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + }}]; + } + [self processViewQueue]; +} + +-(void) replaceIdOnViewQueue:(MLViewID) previousId withBlock:(view_queue_block_t) block havingId:(MLViewID) viewId andAppendOnUnknown:(BOOL) appendOnUnknown withFile:(char*) file andLine:(int) line andFunc:(char*) func +{ + @synchronized(_blockQueue) { + DDLogDebug(@"Replacing block with id %lu with new block having id %lu defined in %s at %@:%d to queue...", (unsigned long)previousId, (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + + //search for old block to replace and remove it + NSInteger index = -1; + for(NSDictionary* blockInfo in _blockQueue) + { + index++; + if(((NSNumber*)blockInfo[@"id"]).unsignedIntegerValue == previousId) + { + DDLogDebug(@"Found blockInfo at index %d: %@", (int)index, blockInfo); + [self->_blockQueue removeObjectAtIndex:index]; + break; + } + } + if(index == -1) + { + if(appendOnUnknown) + { + DDLogDebug(@"Did not find block with id %lu on queue, appending block instead...", (unsigned long)previousId); + [self appendToViewQueue:block withId:viewId andFile:file andLine:line andFunc:func]; + } + else + { + DDLogDebug(@"Did not find block with id %lu on queue, prepending block instead...", (unsigned long)previousId); + [self prependToViewQueue:block withId:viewId andFile:file andLine:line andFunc:func]; + } + return; + } + + //add replaement block at right position + [_blockQueue insertObject:@{@"id":@(viewId), @"block":^(PMKResolver resolve) { + DDLogDebug(@"Calling block with id %lu defined in %s at %@:%d...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + block(resolve); + DDLogDebug(@"Block with id %lu defined in %s at %@:%d finished...", (unsigned long)viewId, func, [HelperTools sanitizeFilePath:file], line); + }} atIndex:index]; + } + [self processViewQueue]; +} + +-(void) processViewQueue +{ + //we are using uikit api all over the place: make sure we always run in the main queue + [HelperTools dispatchAsync:NO reentrantOnQueue:dispatch_get_main_queue() withBlock:^{ + NSMutableArray* viewControllerHierarchy = [self getCurrentViewControllerHierarchy]; + + //don't show the next entry if there is still the previous one + //if(self.splitViewController.collapsed) + if([viewControllerHierarchy count] > 0) + { + DDLogDebug(@"Ignoring call to processViewQueue, already showing: %@", viewControllerHierarchy); + return; + } + + //don't run the next block if the previous one did not yet complete + if(dispatch_semaphore_wait(self->_blockQueueSemaphore, DISPATCH_TIME_NOW) != 0) + { + DDLogDebug(@"Ignoring call to processViewQueue, block still running, showing: %@", viewControllerHierarchy); + return; + } + + NSDictionary* blockInfo = nil; + @synchronized(self->_blockQueue) { + if(self->_blockQueue.count > 0) + { + blockInfo = [self->_blockQueue objectAtIndex:0]; + [self->_blockQueue removeObjectAtIndex:0]; + } + else + DDLogDebug(@"Queue is empty..."); + } + if(blockInfo) + { + //DDLogDebug(@"Calling next block, stacktrace: %@", [NSThread callStackSymbols]); + monal_void_block_t looper = ^{ + dispatch_semaphore_signal(self->_blockQueueSemaphore); + DDLogDebug(@"Looping to next block..."); + [self processViewQueue]; + }; + [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { + ((view_queue_block_t)blockInfo[@"block"])(resolve); + }].ensure(^{ + looper(); + }); + } + else + { + DDLogDebug(@"Not calling next block: there is none..."); + dispatch_semaphore_signal(self->_blockQueueSemaphore); + } + }]; +} + #pragma mark view lifecycle -(void) configureComposeButton @@ -133,6 +301,10 @@ -(void) viewDidLoad self.chatListTable.emptyDataSetSource = self; self.chatListTable.emptyDataSetDelegate = self; + + //has to be done here to not always prepend intro screens onto our view queue + //once a fullscreen view is dismissed (or the app is switched to foreground) + [self segueToIntroScreensIfNeeded]; } -(void) dealloc @@ -409,7 +581,7 @@ -(void) refresh { dispatch_async(dispatch_get_main_queue(), ^{ [self refreshDisplay]; // load contacts - [self segueToIntroScreensIfNeeded]; + [self processViewQueue]; }); } @@ -420,97 +592,134 @@ -(void) didReceiveMemoryWarning -(void) showAddContactWithJid:(NSString*) jid preauthToken:(NSString* _Nullable) preauthToken prefillAccount:(xmpp* _Nullable) account andOmemoFingerprints:(NSDictionary* _Nullable) fingerprints { - dispatch_async(dispatch_get_main_queue(), ^{ - [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ - UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewForJid:jid preauthToken:preauthToken prefillAccount:account andOmemoFingerprints:fingerprints withDismisser:^(MLContact* _Nonnull newContact) { - dispatch_async(dispatch_get_main_queue(), ^{ + appendToViewQueue((^(PMKResolver resolve) { + UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewForJid:jid preauthToken:preauthToken prefillAccount:account andOmemoFingerprints:fingerprints withDismisser:^(MLContact* _Nonnull newContact) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self dismissCompleteViewChainWithAnimation:YES andCompletion:^{ [self presentChatWithContact:newContact]; - }); - }]; - addContactMenuView.ml_disposeCallback = ^{ - [self sheetDismissed]; - }; - [self presentViewController:addContactMenuView animated:NO completion:^{}]; + }]; + }); }]; - }); + addContactMenuView.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:addContactMenuView animated:NO completion:^{resolve(nil);}]; + }]; + })); } -(void) showAddContact { - dispatch_async(dispatch_get_main_queue(), ^{ - [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ - UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewWithDismisser:^(MLContact* _Nonnull newContact) { - dispatch_async(dispatch_get_main_queue(), ^{ + appendToViewQueue((^(PMKResolver resolve) { + UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewWithDismisser:^(MLContact* _Nonnull newContact) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self dismissCompleteViewChainWithAnimation:YES andCompletion:^{ [self presentChatWithContact:newContact]; - }); - }]; - addContactMenuView.ml_disposeCallback = ^{ - [self sheetDismissed]; - }; - [self presentViewController:addContactMenuView animated:NO completion:^{}]; + }]; + }); }]; - }); + addContactMenuView.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:addContactMenuView animated:NO completion:^{resolve(nil);}]; + }]; + })); } -(void) segueToIntroScreensIfNeeded { - //open password migration if needed - NSArray* needingMigration = [[DataLayer sharedInstance] accountListNeedingPasswordMigration]; - if(needingMigration.count > 0) - { - UIViewController* passwordMigration = [[SwiftuiInterface new] makePasswordMigration:needingMigration]; - passwordMigration.ml_disposeCallback = ^{ - [self sheetDismissed]; - }; - [self presentViewController:passwordMigration animated:YES completion:^{}]; - return; - } - - if(![[HelperTools defaultsDB] boolForKey:@"hasCompletedOnboarding"]) - { - [self showOnboarding]; - return; - } - - if(self.enqueueGeneralSettings) - { - self.enqueueGeneralSettings = NO; - [self showGeneralSettings]; - return; - } - + DDLogDebug(@"segueToIntroScreensIfNeeded got called..."); + //prepend in a prepend block to make sure we have prepended everything in order before showing the first view + //(if we would not do this, the first view prepended would be shown regardless of other views prepended after it) + //every entry in here is flipped, because we want to prepend all intro screens to our queue + prependToViewQueue((^(PMKResolver resolve) { #ifdef IS_QUICKSY - if([[DataLayer sharedInstance] enabledAccountCnts].intValue == 0) - { - UIViewController* view = [[SwiftuiInterface new] makeAccountRegistration:@{}]; - if(UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPad) - view.modalPresentationStyle = UIModalPresentationFullScreen; - else - view.ml_disposeCallback = ^{ - [self sheetDismissed]; - }; - [self presentViewController:view animated:NO completion:^{}]; - return; - } + prependToViewQueue((^(PMKResolver resolve) { + [self syncContacts]; + resolve(nil); + })); #else - // display quick start if the user never seen it or if there are 0 enabled accounts - if([[DataLayer sharedInstance] enabledAccountCnts].intValue == 0 && !_loginAlreadyAutodisplayed) - { - UIViewController* loginViewController = [[SwiftuiInterface new] makeViewWithName:@"WelcomeLogIn"]; - loginViewController.ml_disposeCallback = ^{ - self->_loginAlreadyAutodisplayed = YES; - [self sheetDismissed]; - }; - [self presentViewController:loginViewController animated:YES completion:^{}]; - return; - } + [self showWarningsIfNeeded]; #endif - - [self showWarningsIfNeeded]; - + + prependToViewQueue(MLViewIDWelcomeLoginView, (^(PMKResolver resolve) { #ifdef IS_QUICKSY - [self syncContacts]; + if([[DataLayer sharedInstance] enabledAccountCnts].intValue == 0) + { + DDLogDebug(@"Showing account registration view..."); + UIViewController* view = [[SwiftuiInterface new] makeAccountRegistration:@{}]; + if(UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPad) + view.modalPresentationStyle = UIModalPresentationFullScreen; + else + view.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:view animated:NO completion:^{resolve(nil);}]; + }]; + } + else + resolve(nil); +#else + // display quick start if the user never seen it or if there are 0 enabled accounts + if([[DataLayer sharedInstance] enabledAccountCnts].intValue == 0 && !self->_loginAlreadyAutodisplayed) + { + DDLogDebug(@"Showing WelcomeLogIn view..."); + UIViewController* loginViewController = [[SwiftuiInterface new] makeViewWithName:@"WelcomeLogIn"]; + loginViewController.ml_disposeCallback = ^{ + self->_loginAlreadyAutodisplayed = YES; + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:loginViewController animated:YES completion:^{resolve(nil);}]; + }]; + } + else + resolve(nil); #endif + })); + + prependToViewQueue((^(PMKResolver resolve) { + if(![[HelperTools defaultsDB] boolForKey:@"hasCompletedOnboarding"]) + { + DDLogDebug(@"Showing onboarding view..."); + UIViewController* view = [[SwiftuiInterface new] makeViewWithName:@"OnboardingView"]; + if(UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPad) + view.modalPresentationStyle = UIModalPresentationFullScreen; + else + view.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:view animated:NO completion:^{resolve(nil);}]; + }]; + } + else + resolve(nil); + })); + + prependToViewQueue((^(PMKResolver resolve) { + //open password migration if needed + NSArray* needingMigration = [[DataLayer sharedInstance] accountListNeedingPasswordMigration]; + if(needingMigration.count > 0) + { + DDLogDebug(@"Showing password migration view..."); + UIViewController* passwordMigration = [[SwiftuiInterface new] makePasswordMigration:needingMigration]; + passwordMigration.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:passwordMigration animated:YES completion:^{resolve(nil);}]; + }]; + } + else + resolve(nil); + })); + + resolve(nil); + })); } #ifdef IS_QUICKSY @@ -569,57 +778,88 @@ -(void) syncContacts -(void) showWarningsIfNeeded { - dispatch_async(dispatch_get_main_queue(), ^{ - for(NSDictionary* accountDict in [[DataLayer sharedInstance] enabledAccountList]) - { - NSNumber* accountNo = accountDict[kAccountID]; - xmpp* account = [[MLXMPPManager sharedInstance] getConnectedAccountForID:accountNo]; - if(!account) - @throw [NSException exceptionWithName:@"RuntimeException" reason:@"Connected xmpp* object for accountNo is nil!" userInfo:accountDict]; - + for(NSDictionary* accountDict in [[DataLayer sharedInstance] enabledAccountList]) + { + NSNumber* accountNo = accountDict[kAccountID]; + xmpp* account = [[MLXMPPManager sharedInstance] getConnectedAccountForID:accountNo]; + if(!account) + @throw [NSException exceptionWithName:@"RuntimeException" reason:@"Connected xmpp* object for accountNo is nil!" userInfo:accountDict]; + + prependToViewQueue((^(PMKResolver resolve) { if(![_mamWarningDisplayed containsObject:accountNo] && account.accountState >= kStateBound && account.connectionProperties.accountDiscoDone) { if(![account.connectionProperties.accountDiscoFeatures containsObject:@"urn:xmpp:mam:2"]) { + DDLogDebug(@"Showing MAM not supported warning..."); UIAlertController* messageAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Account %@", @""), account.connectionProperties.identity.jid] message:NSLocalizedString(@"Your server does not support MAM (XEP-0313). That means you could frequently miss incoming messages!! You should switch your server or talk to the server admin to enable this!", @"") preferredStyle:UIAlertControllerStyleAlert]; [messageAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction* action __unused) { [_mamWarningDisplayed addObject:accountNo]; + resolve(nil); }]]; - [self presentViewController:messageAlert animated:YES completion:nil]; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:messageAlert animated:YES completion:nil]; + }]; } else + { [_mamWarningDisplayed addObject:accountNo]; + resolve(nil); + } } - + else + resolve(nil); + })); + + prependToViewQueue((^(PMKResolver resolve) { if(![_smacksWarningDisplayed containsObject:accountNo] && account.accountState >= kStateBound) { if(!account.connectionProperties.supportsSM3) { + DDLogDebug(@"Showing smacks not supported warning..."); UIAlertController* messageAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Account %@", @""), account.connectionProperties.identity.jid] message:NSLocalizedString(@"Your server does not support Stream Management (XEP-0198). That means your outgoing messages can get lost frequently!! You should switch your server or talk to the server admin to enable this!", @"") preferredStyle:UIAlertControllerStyleAlert]; [messageAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction* action __unused) { [_smacksWarningDisplayed addObject:accountNo]; + resolve(nil); }]]; - [self presentViewController:messageAlert animated:YES completion:nil]; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:messageAlert animated:YES completion:nil]; + }]; } else + { [_smacksWarningDisplayed addObject:accountNo]; + resolve(nil); + } } - + else + resolve(nil); + })); + + prependToViewQueue((^(PMKResolver resolve) { if(![_pushWarningDisplayed containsObject:accountNo] && account.accountState >= kStateBound && account.connectionProperties.accountDiscoDone) { if(![account.connectionProperties.accountDiscoFeatures containsObject:@"urn:xmpp:push:0"]) { + DDLogDebug(@"Showing push not supported warning..."); UIAlertController* messageAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Account %@", @""), account.connectionProperties.identity.jid] message:NSLocalizedString(@"Your server does not support PUSH (XEP-0357). That means you have to manually open the app to retrieve new incoming messages!! You should switch your server or talk to the server admin to enable this!", @"") preferredStyle:UIAlertControllerStyleAlert]; [messageAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction* action __unused) { [_pushWarningDisplayed addObject:accountNo]; + resolve(nil); }]]; - [self presentViewController:messageAlert animated:YES completion:nil]; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:messageAlert animated:YES completion:nil]; + }]; } else + { [_pushWarningDisplayed addObject:accountNo]; + resolve(nil); + } } - } - }); + else + resolve(nil); + })); + } } -(void) presentSplitPlaceholder @@ -640,45 +880,51 @@ -(void) showNotificationSettings view.ml_disposeCallback = ^{ [self sheetDismissed]; }; - [self presentViewController:view animated:YES completion:^{}]; + [self presentViewController:view animated:YES completion:nil]; }]; } --(void) showGeneralSettings +-(void) prependGeneralSettings { - [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + prependToViewQueue((^(PMKResolver resolve) { UIViewController* view = [[SwiftuiInterface new] makeViewWithName:@"ActiveChatsGeneralSettings"]; view.ml_disposeCallback = ^{ [self sheetDismissed]; }; - [self presentViewController:view animated:YES completion:^{}]; - }]; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:view animated:YES completion:^{resolve(nil);}]; + }]; + })); } --(void) showOnboarding +-(void) showGeneralSettings { - [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ - UIViewController* view = [[SwiftuiInterface new] makeViewWithName:@"OnboardingView"]; - if(UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPad) - view.modalPresentationStyle = UIModalPresentationFullScreen; - else - view.ml_disposeCallback = ^{ - [self sheetDismissed]; - }; - [self presentViewController:view animated:NO completion:^{}]; - }]; + appendToViewQueue((^(PMKResolver resolve) { + UIViewController* view = [[SwiftuiInterface new] makeViewWithName:@"ActiveChatsGeneralSettings"]; + view.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:view animated:YES completion:^{resolve(nil);}]; + }]; + })); } -(void) showSettings { - [self performSegueWithIdentifier:@"showSettings" sender:self]; + appendToViewQueue((^(PMKResolver resolve) { + [self performSegueWithIdentifier:@"showSettings" sender:self]; + resolve(nil); + })); } -(void) showCallContactNotFoundAlert:(NSString*) jid { UIAlertController* messageAlert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Contact not found", @"") message:[NSString stringWithFormat:NSLocalizedString(@"You tried to call contact '%@' but this contact could not be found in your contact list.", @""), jid] preferredStyle:UIAlertControllerStyleAlert]; [messageAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction* action __unused) {}]]; - [self presentViewController:messageAlert animated:YES completion:nil]; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:messageAlert animated:NO completion:nil]; + }]; } -(void) callContact:(MLContact*) contact withCallType:(MLCallType) callType @@ -754,51 +1000,48 @@ -(void) presentChatWithContact:(MLContact*) contact andCompletion:(monal_id_bloc { DDLogVerbose(@"presenting chat with contact: %@, stacktrace: %@", contact, [NSThread callStackSymbols]); dispatch_async(dispatch_get_main_queue(), ^{ - DDLogVerbose(@"presenting chat with contact: %@", contact); - [self dismissCompleteViewChainWithAnimation:YES andCompletion:^{ - // only open contact chat when it is not opened yet (needed for opening via notifications and for macOS) - if([contact isEqualToContact:[MLNotificationManager sharedInstance].currentContact]) - { - // make sure the already open chat is reloaded and return - [[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil]; - if(completion != nil) - completion(@YES); - return; - } - - // clear old chat before opening a new one (but not for splitView == YES) - if(self.splitViewController.collapsed) - [self.navigationController popViewControllerAnimated:NO]; - - // show placeholder if contact is nil, open chat otherwise - if(contact == nil) - { - [self presentSplitPlaceholder]; - if(completion != nil) - completion(@NO); - return; - } + // only open contact chat when it is not opened yet (needed for opening via notifications and for macOS) + if([contact isEqualToContact:[MLNotificationManager sharedInstance].currentContact]) + { + // make sure the already open chat is reloaded and return + [[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil]; + if(completion != nil) + completion(@YES); + return; + } + + // clear old chat before opening a new one (but not for splitView == YES) + if(self.splitViewController.collapsed) + [self.navigationController popViewControllerAnimated:NO]; + + // show placeholder if contact is nil, open chat otherwise + if(contact == nil) + { + [self presentSplitPlaceholder]; + if(completion != nil) + completion(@NO); + return; + } - //open chat (make sure we have an active buddy for it and add it to our ui, if needed) - //but don't animate this if the contact is already present in our list - [[DataLayer sharedInstance] addActiveBuddies:contact.contactJid forAccount:contact.accountId]; - if([[self getChatArrayForSection:pinnedChats] containsObject:contact] || [[self getChatArrayForSection:unpinnedChats] containsObject:contact]) - { + //open chat (make sure we have an active buddy for it and add it to our ui, if needed) + //but don't animate this if the contact is already present in our list + [[DataLayer sharedInstance] addActiveBuddies:contact.contactJid forAccount:contact.accountId]; + if([[self getChatArrayForSection:pinnedChats] containsObject:contact] || [[self getChatArrayForSection:unpinnedChats] containsObject:contact]) + { + [self scrollToContact:contact]; + [self performSegueWithIdentifier:@"showConversation" sender:contact]; + if(completion != nil) + completion(@YES); + } + else + { + [self insertOrMoveContact:contact completion:^(BOOL finished __unused) { [self scrollToContact:contact]; [self performSegueWithIdentifier:@"showConversation" sender:contact]; if(completion != nil) completion(@YES); - } - else - { - [self insertOrMoveContact:contact completion:^(BOOL finished __unused) { - [self scrollToContact:contact]; - [self performSegueWithIdentifier:@"showConversation" sender:contact]; - if(completion != nil) - completion(@YES); - }]; - } - }]; + }]; + } }); } @@ -861,8 +1104,12 @@ -(void) prepareForSegue:(UIStoryboardSegue*) segue sender:(id) sender UINavigationController* nav = segue.destinationViewController; ContactsViewController* contacts = (ContactsViewController*)nav.topViewController; contacts.selectContact = ^(MLContact* selectedContact) { - DDLogVerbose(@"Got selected contact from contactlist ui: %@", selectedContact); - [self presentChatWithContact:selectedContact]; + dispatch_async(dispatch_get_main_queue(), ^{ + DDLogVerbose(@"Got selected contact from contactlist ui: %@", selectedContact); + [self dismissCompleteViewChainWithAnimation:YES andCompletion:^{ + [self presentChatWithContact:selectedContact]; + }]; + }); }; } } @@ -1152,10 +1399,12 @@ -(void) showContacts:(id) sender { // function definition for @selector -(void) showContacts { - // Only segue if at least one account is enabled - if([self showAccountNumberWarningIfNeeded]) - return; - [self performSegueWithIdentifier:@"showContacts" sender:self]; + appendToViewQueue((^(PMKResolver resolve) { + // Only segue if at least one account is enabled + if(![self showAccountNumberWarningIfNeeded]); + [self performSegueWithIdentifier:@"showContacts" sender:self]; + resolve(nil); + })); } //we can not call this var "completion" because then some dumb comiler check kicks in and tells us "completion handler is never called" @@ -1163,7 +1412,7 @@ -(void) showContacts //so this makes it compile again -(void) showRegisterWithUsername:(NSString*) username onHost:(NSString*) host withToken:(NSString*) token usingCompletion:(monal_id_block_t) callback { - [self dismissCompleteViewChainWithAnimation:YES andCompletion:^{ + prependingReplaceOnViewQueue(MLViewIDWelcomeLoginView, MLViewIDRegisterView, (^(PMKResolver resolve) { UIViewController* registerViewController = [[SwiftuiInterface new] makeAccountRegistration:@{ @"host": nilWrapper(host), @"username": nilWrapper(username), @@ -1175,20 +1424,28 @@ -(void) showRegisterWithUsername:(NSString*) username onHost:(NSString*) host wi registerViewController.ml_disposeCallback = ^{ [self sheetDismissed]; }; - [self presentViewController:registerViewController animated:YES completion:^{}]; - }]; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:registerViewController animated:YES completion:^{resolve(nil);}]; + }]; + })); } -(void) showDetails { - if([MLNotificationManager sharedInstance].currentContact != nil) - { - UIViewController* detailsViewController = [[SwiftuiInterface new] makeContactDetails:[MLNotificationManager sharedInstance].currentContact]; - detailsViewController.ml_disposeCallback = ^{ - [self sheetDismissed]; - }; - [self presentViewController:detailsViewController animated:YES completion:^{}]; - } + appendToViewQueue((^(PMKResolver resolve) { + if([MLNotificationManager sharedInstance].currentContact != nil) + { + UIViewController* detailsViewController = [[SwiftuiInterface new] makeContactDetails:[MLNotificationManager sharedInstance].currentContact]; + detailsViewController.ml_disposeCallback = ^{ + [self sheetDismissed]; + }; + [self dismissCompleteViewChainWithAnimation:NO andCompletion:^{ + [self presentViewController:detailsViewController animated:YES completion:^{resolve(nil);}]; + }]; + } + else + resolve(nil); + })); } -(void) deleteConversation @@ -1212,7 +1469,7 @@ -(void) deleteConversation } } --(void) dismissCompleteViewChainWithAnimation:(BOOL) animation andCompletion:(monal_void_block_t _Nullable) completion +-(NSMutableArray*) getCurrentViewControllerHierarchy { MonalAppDelegate* appDelegate = (MonalAppDelegate *)[[UIApplication sharedApplication] delegate]; UIViewController* rootViewController = appDelegate.window.rootViewController; @@ -1222,8 +1479,12 @@ -(void) dismissCompleteViewChainWithAnimation:(BOOL) animation andCompletion:(mo [viewControllers addObject:rootViewController.presentedViewController]; rootViewController = rootViewController.presentedViewController; } - viewControllers = [[[viewControllers reverseObjectEnumerator] allObjects] mutableCopy]; - + return [[[viewControllers reverseObjectEnumerator] allObjects] mutableCopy]; +} + +-(void) dismissCompleteViewChainWithAnimation:(BOOL) animation andCompletion:(monal_void_block_t _Nullable) completion +{ + NSMutableArray* viewControllers = [self getCurrentViewControllerHierarchy]; DDLogVerbose(@"Dismissing view controller hierarchy: %@", viewControllers); [self dismissRecursorWithViewControllers:viewControllers animation:animation andCompletion:completion]; } diff --git a/Monal/Classes/BoardingCards.swift b/Monal/Classes/BoardingCards.swift index 35edf93fc..feb3d4eca 100644 --- a/Monal/Classes/BoardingCards.swift +++ b/Monal/Classes/BoardingCards.swift @@ -236,10 +236,10 @@ struct TakeMeToSettingsView: View { Button(action: { let appDelegate = UIApplication.shared.delegate as! MonalAppDelegate if let activeChats = appDelegate.activeChats { - activeChats.enqueueGeneralSettings = true + activeChats.prependGeneralSettings() } onboardingState.hasCompletedOnboarding = true - delegate.dismiss() + delegate.dismissWithoutAnimation() }) { Text("Take me to settings") .fontWeight(.bold) From a8eaec64dc7d0dd42274be5029f9f2c5533911f4 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 07:50:45 +0200 Subject: [PATCH 16/38] Improve handling of xmpp: uris when already in roster --- Monal/Classes/ActiveChatsViewController.m | 15 +++++++++++++++ Monal/Classes/AddContactMenu.swift | 2 ++ 2 files changed, 17 insertions(+) diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index 996f22c55..e616e4dec 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -592,6 +592,21 @@ -(void) didReceiveMemoryWarning -(void) showAddContactWithJid:(NSString*) jid preauthToken:(NSString* _Nullable) preauthToken prefillAccount:(xmpp* _Nullable) account andOmemoFingerprints:(NSDictionary* _Nullable) fingerprints { + //check if contact is already known in any of our accounts and open a chat with the first contact we can find + for(xmpp* checkAccount in [MLXMPPManager sharedInstance].connectedXMPP) + { + MLContact* checkContact = [MLContact createContactFromJid:jid andAccountNo:checkAccount.accountNo]; + if(checkContact.isInRoster) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [self dismissCompleteViewChainWithAnimation:YES andCompletion:^{ + [self presentChatWithContact:checkContact]; + }]; + }); + return; + } + } + appendToViewQueue((^(PMKResolver resolve) { UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewForJid:jid preauthToken:preauthToken prefillAccount:account andOmemoFingerprints:fingerprints withDismisser:^(MLContact* _Nonnull newContact) { dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/Monal/Classes/AddContactMenu.swift b/Monal/Classes/AddContactMenu.swift index c2ee2385a..6aeb0d78a 100644 --- a/Monal/Classes/AddContactMenu.swift +++ b/Monal/Classes/AddContactMenu.swift @@ -127,8 +127,10 @@ struct AddContactMenu: View { //only alert of already known contact if we did not import the omemo fingerprints if !self.importScannedFingerprints || self.scannedFingerprints?.count ?? 0 == 0 { if self.connectedAccounts.count > 1 { + self.success = true successAlert(title: Text("Already present"), message: Text("This contact is already in the contact list of the selected account")) } else { + self.success = true successAlert(title: Text("Already present"), message: Text("This contact is already in your contact list")) } } From d650e22b7188df6093f36817ec9791fefafb0062 Mon Sep 17 00:00:00 2001 From: nautilusx Date: Sun, 28 Jul 2024 13:52:35 +0200 Subject: [PATCH 17/38] Create description.txt German translation --- appstore_metadata/de-DE/description.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 appstore_metadata/de-DE/description.txt diff --git a/appstore_metadata/de-DE/description.txt b/appstore_metadata/de-DE/description.txt new file mode 100644 index 000000000..04373ec66 --- /dev/null +++ b/appstore_metadata/de-DE/description.txt @@ -0,0 +1,20 @@ +Es gab noch nie einen besseren Zeitpunkt, um in XMPP einzusteigen, ein kostenloses öffentliches Chat-Netzwerk, das niemand kontrolliert oder besitzt. Monal ist ein schneller und benutzerfreundlicher Weg zur Nutzung von XMPP. Lade einfach die App herunter, melde dich an oder registriere dich, und schon kannst du in wenigen Minuten chatten. + +Wesentliche Merkmale: +- Open Source +- Keine Werbung! Starker Fokus auf Privatsphäre. Ruft nicht zu Hause an und hat keine "Messdaten"-Software. +- Liest keine persönlichen Informationen aus. +- Mit einer direkten Verbindung zu deinem Server, werden dein Passwort und alle anderen Informationen niemals an Dritte gesendet. +- OMEMO verschlüsselter Chat +- Funktioniert mit XMPP-Servern von Unternehmen, die VPN erfordern +- Mehrbenutzer-Chats in Gruppen oder Channels +- Audio/Video-Anrufe + +Implementiert bestimmte XMPP-Erweiterungen, um die mobile Kommunikation zu verbessern. +- XEP-0357: Push-Benachrichtigungen +- XEP-0280: Nachrichtenkopien halten Nachrichten zwischen Clients synchron +- XEP-0198: Stream-Management zum schnellen Wiederverbinden +- XEP-0199: XMPP-Ping zur Aufrechterhaltung von Verbindungen +- XEP-0313: Nachrichtenarchivverwaltung zum Herunterladen des Chatverlaufs +- XEP-0352: Client-Statusanzeige zur drastischen Reduzierung des Stromverbrauchs +- XEP-0363: HTTP-Dateitransfer zum Senden von Bildern in Unterhaltungen From 0d59cf435dda5fd8ceb4c91f441e16e2786c61ce Mon Sep 17 00:00:00 2001 From: nautilusx Date: Sun, 28 Jul 2024 13:56:13 +0200 Subject: [PATCH 18/38] Create keywords.txt German translation --- appstore_metadata/de-DE/keywords.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 appstore_metadata/de-DE/keywords.txt diff --git a/appstore_metadata/de-DE/keywords.txt b/appstore_metadata/de-DE/keywords.txt new file mode 100644 index 000000000..4c718ed9e --- /dev/null +++ b/appstore_metadata/de-DE/keywords.txt @@ -0,0 +1 @@ +xmpp, jabber, Chat, messenger, instant messaging, messaging, ejabberd, prosody, OMEMO From 75236545bc1461bc54f2c7b1d7f971b2f6b05f8b Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 08:41:31 +0200 Subject: [PATCH 19/38] Add missing appstore metadata --- appstore_metadata/de-DE/marketing_url.txt | 1 + appstore_metadata/de-DE/privacy_url.txt | 1 + appstore_metadata/de-DE/support_url.txt | 1 + appstore_metadata/en-US/marketing_url.txt | 1 + appstore_metadata/en-US/privacy_url.txt | 1 + appstore_metadata/en-US/support_url.txt | 1 + 6 files changed, 6 insertions(+) create mode 100644 appstore_metadata/de-DE/marketing_url.txt create mode 100644 appstore_metadata/de-DE/privacy_url.txt create mode 100644 appstore_metadata/de-DE/support_url.txt create mode 100644 appstore_metadata/en-US/marketing_url.txt create mode 100644 appstore_metadata/en-US/privacy_url.txt create mode 100644 appstore_metadata/en-US/support_url.txt diff --git a/appstore_metadata/de-DE/marketing_url.txt b/appstore_metadata/de-DE/marketing_url.txt new file mode 100644 index 000000000..43952ebe2 --- /dev/null +++ b/appstore_metadata/de-DE/marketing_url.txt @@ -0,0 +1 @@ +https://monal-im.org/ \ No newline at end of file diff --git a/appstore_metadata/de-DE/privacy_url.txt b/appstore_metadata/de-DE/privacy_url.txt new file mode 100644 index 000000000..4c9b027f0 --- /dev/null +++ b/appstore_metadata/de-DE/privacy_url.txt @@ -0,0 +1 @@ +https://monal-im.org/privacy/ \ No newline at end of file diff --git a/appstore_metadata/de-DE/support_url.txt b/appstore_metadata/de-DE/support_url.txt new file mode 100644 index 000000000..1ffe2f0bb --- /dev/null +++ b/appstore_metadata/de-DE/support_url.txt @@ -0,0 +1 @@ +https://monal-im.org/support/ \ No newline at end of file diff --git a/appstore_metadata/en-US/marketing_url.txt b/appstore_metadata/en-US/marketing_url.txt new file mode 100644 index 000000000..43952ebe2 --- /dev/null +++ b/appstore_metadata/en-US/marketing_url.txt @@ -0,0 +1 @@ +https://monal-im.org/ \ No newline at end of file diff --git a/appstore_metadata/en-US/privacy_url.txt b/appstore_metadata/en-US/privacy_url.txt new file mode 100644 index 000000000..4c9b027f0 --- /dev/null +++ b/appstore_metadata/en-US/privacy_url.txt @@ -0,0 +1 @@ +https://monal-im.org/privacy/ \ No newline at end of file diff --git a/appstore_metadata/en-US/support_url.txt b/appstore_metadata/en-US/support_url.txt new file mode 100644 index 000000000..1ffe2f0bb --- /dev/null +++ b/appstore_metadata/en-US/support_url.txt @@ -0,0 +1 @@ +https://monal-im.org/support/ \ No newline at end of file From 3c7cb32881cc85d99f935f53f864c37f532051ec Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 08:48:37 +0200 Subject: [PATCH 20/38] Revert "Use an uncompressed PNG if image upload quality is set to 100%" This reverts commit ff0b3fe546de990b706802d12016dcb9ee33c0d2. --- Monal/Classes/MLFiletransfer.m | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Monal/Classes/MLFiletransfer.m b/Monal/Classes/MLFiletransfer.m index 78ef4f634..8a99d8f48 100644 --- a/Monal/Classes/MLFiletransfer.m +++ b/Monal/Classes/MLFiletransfer.m @@ -623,17 +623,8 @@ +(MLHandler*) prepareUIImageUpload:(UIImage*) image NSString* tempname = [NSString stringWithFormat:@"tmp.%@", [[NSUUID UUID] UUIDString]]; NSError* error; NSString* file = [_documentCacheDir stringByAppendingPathComponent:tempname]; - NSData* imageData = nil; - if(imageQuality == 1.0) - { - DDLogDebug(@"Image upload quality was set to 100%%, tempstoring png encoded file at %@", file); - imageData = UIImagePNGRepresentation(image); - } - else - { - DDLogDebug(@"Tempstoring jpeg encoded file having quality %f at %@", imageQuality, file); - imageData = UIImageJPEGRepresentation(image, imageQuality); - } + DDLogDebug(@"Tempstoring jpeg encoded file having quality %f at %@", imageQuality, file); + NSData* imageData = UIImageJPEGRepresentation(image, imageQuality); [imageData writeToFile:file options:NSDataWritingAtomic error:&error]; if(error) { From 4237a1f7379eeb352883fc3241700668edc1df44 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Mon, 29 Jul 2024 09:27:30 +0200 Subject: [PATCH 21/38] Add setting to send images as original --- Monal/Classes/GeneralSettings.swift | 36 ++++++++++++++++++----------- Monal/Classes/HelperTools.m | 34 +++++++++++++++++---------- Monal/Classes/MLXMPPManager.m | 4 +++- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Monal/Classes/GeneralSettings.swift b/Monal/Classes/GeneralSettings.swift index 20f06a990..e55873b12 100644 --- a/Monal/Classes/GeneralSettings.swift +++ b/Monal/Classes/GeneralSettings.swift @@ -115,6 +115,9 @@ class GeneralSettingsDefaultsDB: ObservableObject { @defaultsDB("useDnssecForAllConnections") var useDnssecForAllConnections: Bool + + @defaultsDB("uploadImagesOriginal") + var uploadImagesOriginal: Bool } @@ -493,20 +496,25 @@ struct AttachmentSettings: View { } Section(header: Text("Upload Settings")) { - Text("Adjust the quality of images uploaded") - .foregroundColor(.secondary) - .font(.footnote) - Slider( - value: $generalSettingsDefaultsDB.imageUploadQuality, - in: 0.33...1.0, - step: 0.01, - minimumValueLabel: Text("33%"), - maximumValueLabel: Text("100%"), - label: { - Text("Upload Settings") - } - ) - Text("Image Upload Quality: \(String(format: "%.0f%%", generalSettingsDefaultsDB.imageUploadQuality*100))") + SettingsToggle(isOn: $generalSettingsDefaultsDB.uploadImagesOriginal) { + Text("Upload Original Images") + } + if !generalSettingsDefaultsDB.uploadImagesOriginal { + Text("Adjust the quality of images uploaded") + .foregroundColor(.secondary) + .font(.footnote) + Slider( + value: $generalSettingsDefaultsDB.imageUploadQuality, + in: 0.33...1.0, + step: 0.01, + minimumValueLabel: Text("33%"), + maximumValueLabel: Text("100%"), + label: { + Text("Upload Settings") + } + ) + Text("Image Upload JPEG-Quality: \(String(format: "%.0f%%", generalSettingsDefaultsDB.imageUploadQuality*100))") + } } } } diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index a5ca0221f..a58a0a58c 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -1040,8 +1040,13 @@ +(void) handleUploadItemProvider:(NSItemProvider*) provider withCompletionHandle } DDLogInfo(@"Got memory image item: %@", item); payload[@"type"] = @"image"; - //use prepareUIImageUpload to resize the image to the configured quality - payload[@"data"] = [MLFiletransfer prepareUIImageUpload:item]; + if(![[HelperTools defaultsDB] boolForKey:@"uploadImagesOriginal"]) + { + //use prepareUIImageUpload to resize the image to the configured quality + payload[@"data"] = [MLFiletransfer prepareUIImageUpload:item]; + } + else + payload[@"data"] = [MLFiletransfer prepareDataUpload:UIImagePNGRepresentation(item) withFileExtension:@"png"]; payload[@"preview"] = item; return completion(payload); }]; @@ -1050,16 +1055,21 @@ +(void) handleUploadItemProvider:(NSItemProvider*) provider withCompletionHandle { DDLogInfo(@"Got image item: %@", item); payload[@"type"] = @"image"; - [item startAccessingSecurityScopedResource]; - [[NSFileCoordinator new] coordinateReadingItemAtURL:item options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL* _Nonnull newURL) { - DDLogDebug(@"NSFileCoordinator called accessor for image: %@", newURL); - UIImage* image = [UIImage imageWithContentsOfFile:[newURL path]]; - DDLogDebug(@"Created UIImage: %@", image); - //use prepareUIImageUpload to resize the image to the configured quality (instead of just uploading the raw image file) - payload[@"data"] = [MLFiletransfer prepareUIImageUpload:image]; - //we can not use newURL here, because it will fall out of scope while the preview is rendered in another thread - return [HelperTools addUploadItemPreviewForItem:item provider:provider andPayload:payload withCompletionHandler:completion]; - }]; + if(![[HelperTools defaultsDB] boolForKey:@"uploadImagesOriginal"]) + { + [item startAccessingSecurityScopedResource]; + [[NSFileCoordinator new] coordinateReadingItemAtURL:item options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL* _Nonnull newURL) { + DDLogDebug(@"NSFileCoordinator called accessor for image: %@", newURL); + UIImage* image = [UIImage imageWithContentsOfFile:[newURL path]]; + DDLogDebug(@"Created UIImage: %@", image); + //use prepareUIImageUpload to resize the image to the configured quality (instead of just uploading the raw image file) + payload[@"data"] = [MLFiletransfer prepareUIImageUpload:image]; + //we can not use newURL here, because it will fall out of scope while the preview is rendered in another thread + return [HelperTools addUploadItemPreviewForItem:item provider:provider andPayload:payload withCompletionHandler:completion]; + }]; + } + else + return prepareFile(item); if(error != nil) { DDLogError(@"Error preparing file coordinator: %@", error); diff --git a/Monal/Classes/MLXMPPManager.m b/Monal/Classes/MLXMPPManager.m index 1fb869799..cfe064a7e 100644 --- a/Monal/Classes/MLXMPPManager.m +++ b/Monal/Classes/MLXMPPManager.m @@ -92,7 +92,7 @@ -(void) defaultSettings [self upgradeIntegerUserSettingsIfUnset:@"AutodownloadFiletransfersWifiMaxSize" toDefault:32*1024*1024]; // 32 MiB // upgrade default image quality - [self upgradeFloatUserSettingsIfUnset:@"ImageUploadQuality" toDefault:0.75]; + [self upgradeFloatUserSettingsIfUnset:@"ImageUploadQuality" toDefault:0.50]; // remove old settings from shareSheet outbox [self removeObjectUserSettingsIfSet:@"lastRecipient"]; @@ -147,6 +147,8 @@ -(void) defaultSettings [self upgradeBoolUserSettingsIfUnset:@"hasCompletedOnboarding" toDefault:NO]; + [self upgradeBoolUserSettingsIfUnset:@"uploadImagesOriginal" toDefault:NO]; + // //always show onboarding on simulator for now // #if TARGET_OS_SIMULATOR // [[HelperTools defaultsDB] setBool:NO forKey:@"hasCompletedOnboarding"]; From d5443254620bf1291d5bde8ac911e95632468224 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Tue, 30 Jul 2024 03:59:50 +0200 Subject: [PATCH 22/38] Fix fastlane metadata bugs in beta and stable workflows --- .github/workflows/beta.build-push.yml | 29 ++++++++++++++++++++++-- .github/workflows/quicksy.build-push.yml | 7 +++--- .github/workflows/stable.build-push.yml | 10 ++++---- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 49a3e73f7..365bf0c76 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -126,10 +126,35 @@ jobs: run: | buildNumber=$(git tag --sort="v:refname" | grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') git push origin Build_iOS_$buildNumber + - name: Create fastlane whatsNew hash in environment + id: buildinfo + env: + CHANGELOG_IOS: ${{ steps.releasenotes.outputs.notes_ios }} + CHANGELOG_MACOS: ${{ steps.releasenotes.outputs.notes_macos }} + run: | + get_changelog() { + local escaped=$(printf '%s\n' "$1" | jq -sRr @json) + local json="{\"default\":{\"whats_new\":$escaped}," + for dir in ./appstore_metadata/*/; do + dir="$(basename "$dir")" + if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then + json="$json\"${dir%/}\":{\"whats_new\":$escaped}," + fi + done + json="${json%,}}" + echo "$json" + } + echo "buildinfo_ios<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(get_changelog "$CHANGELOG_IOS")" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" + + echo "buildinfo_macos<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(get_changelog "$CHANGELOG_MACOS")" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - name: Publish ios to appstore connect #run: xcrun altool --upload-app -f ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y --team-id S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" env: - PILOT_CHANGELOG: ${{ steps.releasenotes.outputs.notes_ios }} + PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_ios }} run: | fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" distribute_external:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true - name: Notarize catalyst @@ -156,7 +181,7 @@ jobs: - name: Publish catalyst to appstore connect #run: xcrun altool --upload-app --file ./Monal/build/app/Monal.pkg --type macos --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" --primary-bundle-id org.monal-im.prod.catalyst.monal env: - PILOT_CHANGELOG: ${{ steps.releasenotes.outputs.notes_macos }} + PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_macos }} run: | fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" pkg:"./Monal/build/app/Monal.pkg" distribute_external:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true - name: Release diff --git a/.github/workflows/quicksy.build-push.yml b/.github/workflows/quicksy.build-push.yml index b2b60451d..e86eac9b0 100644 --- a/.github/workflows/quicksy.build-push.yml +++ b/.github/workflows/quicksy.build-push.yml @@ -121,10 +121,11 @@ jobs: CHANGELOG: ${{ steps.releasenotes.outputs.notes_ios }} run: | path_ios="$(mktemp -d)" - cp -av ./appstore_metadata/* "$path_ios" + cp -av ./appstore_quicksy_metadata/* "$path_ios" echo -n "$(date +%Y) Thilo Molitor" > "$path_ios/copyright.txt" - for dir in */; do - if [[ -d "$dir" && "$dir" == *-* ]]; then + for dir in ./appstore_quicksy_metadata/*/; do + dir="$(basename "$dir")" + if [[ -d "./appstore_quicksy_metadata/$dir" && "$dir" == *-* ]]; then echo -n "$CHANGELOG_IOS" > "$path_ios/${dir%/}/release_notes.txt" fi done diff --git a/.github/workflows/stable.build-push.yml b/.github/workflows/stable.build-push.yml index 03093a738..4dfef89db 100644 --- a/.github/workflows/stable.build-push.yml +++ b/.github/workflows/stable.build-push.yml @@ -131,8 +131,9 @@ jobs: path_ios="$(mktemp -d)" cp -av ./appstore_metadata/* "$path_ios" echo -n "$(date +%Y) Thilo Molitor" > "$path_ios/copyright.txt" - for dir in */; do - if [[ -d "$dir" && "$dir" == *-* ]]; then + for dir in ./appstore_metadata/*/; do + dir="$(basename "$dir")" + if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then echo -n "$CHANGELOG_IOS" > "$path_ios/${dir%/}/release_notes.txt" fi done @@ -141,8 +142,9 @@ jobs: path_macos="$(mktemp -d)" cp -av ./appstore_metadata/* "$path_macos" echo -n "$(date +%Y) Thilo Molitor" > "$path_macos/copyright.txt" - for dir in */; do - if [[ -d "$dir" && "$dir" == *-* ]]; then + for dir in ./appstore_metadata/*/; do + dir="$(basename "$dir")" + if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then echo -n "$CHANGELOG_MACOS" > "$path_macos/${dir%/}/release_notes.txt" fi done From af73e74651e49070e5730050ae7c59951f5a3fac Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Tue, 30 Jul 2024 14:02:37 +0200 Subject: [PATCH 23/38] Make message deletion label in settings translatable --- Monal/Classes/GeneralSettings.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Monal/Classes/GeneralSettings.swift b/Monal/Classes/GeneralSettings.swift index e55873b12..38929678b 100644 --- a/Monal/Classes/GeneralSettings.swift +++ b/Monal/Classes/GeneralSettings.swift @@ -309,7 +309,7 @@ like hotel wifi, ugly mobile carriers etc. Section(header: Text("On this device")) { VStack(alignment: .leading, spacing: 0) { - Picker("Autodelete all messages older than", selection: $autodeleteIntervalSelection) { + Picker(selection: $autodeleteIntervalSelection, label: Text("Autodelete all messages older than")) { ForEach(autodeleteOptions.keys.sorted(), id: \.self) { key in Text(autodeleteOptions[key]!).tag(key) } From 0dafbfe51c9febe20d743a9aebe40345555f0eed Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Wed, 31 Jul 2024 04:22:47 +0200 Subject: [PATCH 24/38] Update WebRTC lib to 126 --- Monal/Podfile | 6 +----- Monal/Podfile.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Monal/Podfile b/Monal/Podfile index 3c5aaac2d..70376e996 100644 --- a/Monal/Podfile +++ b/Monal/Podfile @@ -32,11 +32,7 @@ def monalxmpp pod 'sqlite3/perf-threadsafe', inhibit_warnings: true pod 'ASN1Decoder' #later versions of the webrtc lib trigger the following app review error: - #The app references non-public symbols in Contents/Frameworks/WebRTC.framework/Versions/A/WebRTC: - #_AVCaptureSessionInterruptionReasonKey, _AVCaptureSessionPresetInputPriority. - #If method names in your source code match the private Apple APIs listed above, altering your method names - #will help prevent this app from being flagged in future submissions. - pod 'WebRTC-lib', '~> 123.0' + pod 'WebRTC-lib' #pod 'GoogleWebRTC' pod 'KSCrash', subspecs:['Recording', 'Reporting/Filters/Sets', 'Reporting/Filters/Tools', 'Reporting/Tools', 'Core'] signalDeps diff --git a/Monal/Podfile.lock b/Monal/Podfile.lock index fed3e2c43..280ccdfe7 100644 --- a/Monal/Podfile.lock +++ b/Monal/Podfile.lock @@ -52,9 +52,9 @@ PODS: - PromiseKit/UIKit (8.1.1): - PromiseKit/CorePromise - SAMKeychain (1.5.3) - - SDWebImage (5.19.2): - - SDWebImage/Core (= 5.19.2) - - SDWebImage/Core (5.19.2) + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) - SignalProtocolC (2.3.3) - SignalProtocolObjC (1.1.1): - SignalProtocolC (~> 2.3.3) @@ -62,7 +62,7 @@ PODS: - "sqlite3/common (3.46.0+1)" - "sqlite3/perf-threadsafe (3.46.0+1)": - sqlite3/common - - WebRTC-lib (123.0.0) + - WebRTC-lib (126.0.0) DEPENDENCIES: - ASN1Decoder @@ -82,7 +82,7 @@ DEPENDENCIES: - SignalProtocolC (from `https://github.com/monal-im/libsignal-protocol-c`, branch `master`) - SignalProtocolObjC (from `https://github.com/monal-im/SignalProtocol-ObjC.git`, branch `master`) - sqlite3/perf-threadsafe - - WebRTC-lib (~> 123.0) + - WebRTC-lib SPEC REPOS: trunk: @@ -128,13 +128,13 @@ SPEC CHECKSUMS: NotificationBannerSwift: dce54ded532b26e30cd8e7f4d80e124a0f2ba7d1 PromiseKit: d1be44b474e5acfa16adf007a1f49f104e10fead SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c - SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d SignalProtocolC: 8092866e45b663a6bc3e45a8d13bad2571dbf236 SignalProtocolObjC: 1beb46b1d35733e7ab96a919f88bac20ec771c73 SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25 sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 - WebRTC-lib: bb973dd47acf5bc48d8a935a92ae836b70599bc1 + WebRTC-lib: 7e2e15d90ebca6e08a1eb5d4afc365d70e4b95b0 -PODFILE CHECKSUM: f766ee234cce3182eaa8a645d3fa1e41666094d2 +PODFILE CHECKSUM: e66b7bd410467c69adaa2ca9b0fca4fb025d8aa4 COCOAPODS: 1.15.2 From 7c40dc249bd79d9325db16e78a571a245971943d Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Wed, 31 Jul 2024 05:05:36 +0200 Subject: [PATCH 25/38] Add workflow to automatically create a PR from beta to stable This PR automatically includes changelog entries from all beta releases since the last stable release and has the version of the latest betarelease as title. --- .github/workflows/create-stable-pr.yml | 122 +++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 .github/workflows/create-stable-pr.yml diff --git a/.github/workflows/create-stable-pr.yml b/.github/workflows/create-stable-pr.yml new file mode 100644 index 000000000..17142b268 --- /dev/null +++ b/.github/workflows/create-stable-pr.yml @@ -0,0 +1,122 @@ +name: Create Pull Request from Beta to Stable + +on: + push: + branches: [ beta ] + workflow_dispatch: + +jobs: + create-pull-request: + runs-on: ubuntu-latest + steps: + - name: Checkout Beta Branch + uses: actions/checkout@v4 + with: + clean: true + submodules: true + fetch-depth: 100 + fetch-tags: true + show-progress: true + lfs: true + ref: beta + - name: Checkout Stable Branch + run: | + git fetch --all + git checkout stable + git branch + - name: Get Merge Commits from Beta not in Stable + id: get_commits + run: | + function repairNotes { + sed 's/\r//g' | awk '{ + if (NR == 1) { + printf("%s", $0) + } else { + if ($0 ~ /^[\t ]*(-|IOS_ONLY[\t ]*-|MACOS_ONLY[\t ]*-).*$/) { + printf("\n%s", $0) + } else { + printf(" %s", $0) + } + } + } + END { + printf("\n") + }' + } + echo "Extracting merge commit texts..." + version="$(git log beta -n 1 --merges --pretty=format:%s | sed -E 's/^[\t\n ]*([^\n\t ]+)[\t\n ]+\(([^\n\t ]+)\)[\t\n ]*$/\1/g')" + echo "version=$version" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "buildVersion=$(echo "$version" | grep -oE '^[0-9]+(\.[0-9]+){0,2}')" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "description<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "$(git log stable..beta --merges --pretty=format:%b)" | repairNotes | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + - name: Find Existing Pull Request + id: find_pr + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); + const { data: pullRequests } = await github.rest.pulls.list({ + owner, + repo, + state: 'open', + head: 'beta', + base: 'stable' + }); + const existingPR = pullRequests.find(pr => pr.labels.some(label => label.name === 'automated-pr')); + console.log(`Existing PR: `, existingPR); + if(existingPR) + return existingPR.number; + else + return null; + - name: Create or Update Pull Request + id: create_or_update_pr + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); + const prNumber = ${{ steps.find_pr.outputs.result }}; + let pullRequest; + if(prNumber) + { + console.log(`Updating old PR #${prNumber}...`); + pullRequest = await github.rest.pulls.update({ + owner, + repo, + pull_number: prNumber, + title: `${{ steps.get_commits.outputs.buildVersion }}`, + body: `${{ steps.get_commits.outputs.description }}`, + }); + console.log(`Updated pull request #${prNumber}`); + } + else + { + console.log(`Creating new PR...`); + pullRequest = await github.rest.pulls.create({ + owner, + repo, + head: 'beta', + base: 'stable', + draft: true, + title: `${{ steps.get_commits.outputs.buildVersion }}`, + body: `${{ steps.get_commits.outputs.description }}`, + }); + console.log(`Created pull request #${pullRequest.data.number}`); + } + return pullRequest.data.number; + - name: Add Label to Pull Request + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); + const pullNumber = ${{ steps.create_or_update_pr.outputs.result }}; + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: pullNumber, + labels: ['automated-pr'] + }); + console.log(`Added label to pull request #${pullNumber}`); From 9717831877203969b19a568f4a8c475857deba79 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Wed, 31 Jul 2024 18:56:27 +0200 Subject: [PATCH 26/38] Fix beta build workflow --- .github/workflows/beta.build-push.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 365bf0c76..51a4b0547 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -144,13 +144,13 @@ jobs: json="${json%,}}" echo "$json" } - echo "buildinfo_ios<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(get_changelog "$CHANGELOG_IOS")" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "buildinfo_ios<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "$(get_changelog "$CHANGELOG_IOS")" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - echo "buildinfo_macos<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(get_changelog "$CHANGELOG_MACOS")" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "buildinfo_macos<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "$(get_changelog "$CHANGELOG_MACOS")" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - name: Publish ios to appstore connect #run: xcrun altool --upload-app -f ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y --team-id S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" env: From 2e6265112ff8543aea400966589dcc64766051e7 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Wed, 31 Jul 2024 19:47:23 +0200 Subject: [PATCH 27/38] Add beta changelog back in --- .github/workflows/beta.build-push.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 51a4b0547..0a46966e2 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -155,6 +155,7 @@ jobs: #run: xcrun altool --upload-app -f ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y --team-id S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" env: PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_ios }} + PILOT_CHANGELOG: ${{ steps.releasenotes.outputs.notes_ios }} run: | fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" distribute_external:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true - name: Notarize catalyst @@ -182,6 +183,7 @@ jobs: #run: xcrun altool --upload-app --file ./Monal/build/app/Monal.pkg --type macos --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" --primary-bundle-id org.monal-im.prod.catalyst.monal env: PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_macos }} + PILOT_CHANGELOG: ${{ steps.releasenotes.outputs.notes_macos }} run: | fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" pkg:"./Monal/build/app/Monal.pkg" distribute_external:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true - name: Release From f194b05f0cad039123b6108e21bfc5f050228473 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Wed, 31 Jul 2024 19:43:57 +0200 Subject: [PATCH 28/38] Add setting to not link filetransfers into documents directory --- Monal/Classes/GeneralSettings.swift | 8 +++- Monal/Classes/MLFiletransfer.m | 61 +++++++++++++++-------------- Monal/Classes/MLXMPPManager.m | 2 + 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/Monal/Classes/GeneralSettings.swift b/Monal/Classes/GeneralSettings.swift index 38929678b..c4cdc771e 100644 --- a/Monal/Classes/GeneralSettings.swift +++ b/Monal/Classes/GeneralSettings.swift @@ -118,6 +118,9 @@ class GeneralSettingsDefaultsDB: ObservableObject { @defaultsDB("uploadImagesOriginal") var uploadImagesOriginal: Bool + + @defaultsDB("hardlinkFiletransfersIntoDocuments") + var hardlinkFiletransfersIntoDocuments: Bool } @@ -457,7 +460,10 @@ struct AttachmentSettings: View { Form { Section(header: Text("General File Transfer Settings")) { SettingsToggle(isOn: $generalSettingsDefaultsDB.autodownloadFiletransfers) { - Text("Auto-Download Media") + Text("Auto-Download Media and Files") + } + SettingsToggle(isOn: $generalSettingsDefaultsDB.hardlinkFiletransfersIntoDocuments) { + Text("Make transfered Media and Files accessible in Files App") } } diff --git a/Monal/Classes/MLFiletransfer.m b/Monal/Classes/MLFiletransfer.m index 8a99d8f48..c9ed9a054 100644 --- a/Monal/Classes/MLFiletransfer.m +++ b/Monal/Classes/MLFiletransfer.m @@ -358,12 +358,12 @@ -(void) URLSession:(nonnull NSURLSession*) session downloadTask:(nonnull NSURLSe if(error) { DDLogError(@"Could not copy cache file to tmp file: %@", error); - #ifdef DEBUG +#ifdef DEBUG @throw [NSException exceptionWithName:@"ERROR_WHILE_COPYING_CACHEFILE" reason:@"Could not copy cacheFile!" userInfo:@{ @"cacheFile": cacheFile, @"cacheFileTMP": cacheFileTMP }]; - #endif +#endif return; } @@ -371,11 +371,11 @@ -(void) URLSession:(nonnull NSURLSession*) session downloadTask:(nonnull NSURLSe if(error) { DDLogError(@"Could not delete original cache file: %@", error); - #ifdef DEBUG +#ifdef DEBUG @throw [NSException exceptionWithName:@"ERROR_WHILE_DELETING_CACHEFILE" reason:@"Could not delete cacheFile!" userInfo:@{ @"cacheFile": cacheFile }]; - #endif +#endif return; } @@ -383,42 +383,45 @@ -(void) URLSession:(nonnull NSURLSession*) session downloadTask:(nonnull NSURLSe if(error) { DDLogError(@"Could not rename tmp file to cache file: %@", error); - #ifdef DEBUG +#ifdef DEBUG @throw [NSException exceptionWithName:@"ERROR_WHILE_RENAMING_CACHEFILE" reason:@"Could not rename cacheFileTMP to cacheFile!" userInfo:@{ @"cacheFile": cacheFile, @"cacheFileTMP": cacheFileTMP }]; - #endif +#endif return; } } - NSURL* hardLink = [[_fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; - for(NSString* pathComponent in hardlinkPathComponents) - hardLink = [hardLink URLByAppendingPathComponent:pathComponent]; - - DDLogInfo(@"Hardlinking cache file at '%@' into documents directory at '%@'...", cacheFile, hardLink); - if(![_fileManager fileExistsAtPath:[hardLink.URLByDeletingLastPathComponent path]]) + if([[HelperTools defaultsDB] boolForKey:@"hardlinkFiletransfersIntoDocuments"]) { - DDLogVerbose(@"Creating hardlinking dir struct at '%@'...", hardLink.URLByDeletingLastPathComponent); - [_fileManager createDirectoryAtURL:hardLink.URLByDeletingLastPathComponent withIntermediateDirectories:YES attributes:@{NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication} error:&error]; - if(error) - DDLogWarn(@"Ignoring error creating hardlinking dir struct at '%@': %@", hardLink, error); + NSURL* hardLink = [[_fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; + for(NSString* pathComponent in hardlinkPathComponents) + hardLink = [hardLink URLByAppendingPathComponent:pathComponent]; + + DDLogInfo(@"Hardlinking cache file at '%@' into documents directory at '%@'...", cacheFile, hardLink); + if(![_fileManager fileExistsAtPath:[hardLink.URLByDeletingLastPathComponent path]]) + { + DDLogVerbose(@"Creating hardlinking dir struct at '%@'...", hardLink.URLByDeletingLastPathComponent); + [_fileManager createDirectoryAtURL:hardLink.URLByDeletingLastPathComponent withIntermediateDirectories:YES attributes:@{NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication} error:&error]; + if(error) + DDLogWarn(@"Ignoring error creating hardlinking dir struct at '%@': %@", hardLink, error); + else + [HelperTools configureFileProtection:NSFileProtectionCompleteUntilFirstUserAuthentication forFile:[hardLink path]]; + } + + //don't throw any error if the file aready exists, because it could be a rare collision (we only use 16 bit random numbers to keep the file prefix short) + if([_fileManager fileExistsAtPath:[hardLink path]]) + DDLogWarn(@"Not hardlinking file '%@' to '%@': file already exists (maybe a rare collision?)...", cacheFile, hardLink); else - [HelperTools configureFileProtection:NSFileProtectionCompleteUntilFirstUserAuthentication forFile:[hardLink path]]; - } - - //don't throw any error if the file aready exists, because it could be a rare collision (we only use 16 bit random numbers to keep the file prefix short) - if([_fileManager fileExistsAtPath:[hardLink path]]) - DDLogWarn(@"Not hardlinking file '%@' to '%@': file already exists (maybe a rare collision?)...", cacheFile, hardLink); - else - { - DDLogVerbose(@"Hardlinking cache file '%@' to '%@'...", cacheFile, hardLink); - error = [HelperTools hardLinkOrCopyFile:cacheFile to:[hardLink path]]; - if(error) { - DDLogError(@"Error creating hardlink: %@", error); - @throw [NSException exceptionWithName:@"ERROR_WHILE_HARDLINKING_FILE" reason:[NSString stringWithFormat:@"%@", error] userInfo:@{@"error": error}]; + DDLogVerbose(@"Hardlinking cache file '%@' to '%@'...", cacheFile, hardLink); + error = [HelperTools hardLinkOrCopyFile:cacheFile to:[hardLink path]]; + if(error) + { + DDLogError(@"Error creating hardlink: %@", error); + @throw [NSException exceptionWithName:@"ERROR_WHILE_HARDLINKING_FILE" reason:[NSString stringWithFormat:@"%@", error] userInfo:@{@"error": error}]; + } } } } diff --git a/Monal/Classes/MLXMPPManager.m b/Monal/Classes/MLXMPPManager.m index cfe064a7e..c31636676 100644 --- a/Monal/Classes/MLXMPPManager.m +++ b/Monal/Classes/MLXMPPManager.m @@ -149,6 +149,8 @@ -(void) defaultSettings [self upgradeBoolUserSettingsIfUnset:@"uploadImagesOriginal" toDefault:NO]; + [self upgradeBoolUserSettingsIfUnset:@"hardlinkFiletransfersIntoDocuments" toDefault:YES]; + // //always show onboarding on simulator for now // #if TARGET_OS_SIMULATOR // [[HelperTools defaultsDB] setBool:NO forKey:@"hasCompletedOnboarding"]; From b0deefccffd2812d62a356a0e35fb8958dbd7e00 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Wed, 31 Jul 2024 19:47:23 +0200 Subject: [PATCH 29/38] Try to fix workflows --- .github/workflows/beta.build-push.yml | 81 ++++++++++++++++-------- .github/workflows/quicksy.build-push.yml | 9 +-- .github/workflows/stable.build-push.yml | 6 +- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 51a4b0547..2f059537c 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -41,7 +41,7 @@ jobs: run: git submodule update -f --init --remote - name: Get last build tag and increment it run: | - oldBuildNumber=$(git tag --sort="v:refname" |grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') + oldBuildNumber=$(git tag --sort="v:refname" | grep "Build_iOS" | grep -v "Quicksy_Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') buildNumber=$(expr $oldBuildNumber + 1) echo "New buildNumber is $buildNumber" git tag Build_iOS_$buildNumber @@ -64,7 +64,7 @@ jobs: printf("\n") }' } - buildNumber="$(git tag --sort="v:refname" | grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g')" + buildNumber="$(git tag --sort="v:refname" | grep "Build_iOS" | grep -v "Quicksy_Build_iOS" | tail -n1 | sed 's/Build_iOS_//g')" version="$(git log -n 1 --merges --pretty=format:%s | sed -E 's/^[\t\n ]*([^\n\t ]+)[\t\n ]+\(([^\n\t ]+)\)[\t\n ]*$/\1/g')" mkdir -p /Users/ci/releases OUTPUT_FILE="/Users/ci/releases/$buildNumber.output" @@ -124,39 +124,64 @@ jobs: run: xcrun altool --validate-app --file ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" - name: Push beta tag to repo run: | - buildNumber=$(git tag --sort="v:refname" | grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') + buildNumber=$(git tag --sort="v:refname" | grep "Build_iOS" | grep -v "Quicksy_Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') git push origin Build_iOS_$buildNumber - - name: Create fastlane whatsNew hash in environment - id: buildinfo - env: - CHANGELOG_IOS: ${{ steps.releasenotes.outputs.notes_ios }} - CHANGELOG_MACOS: ${{ steps.releasenotes.outputs.notes_macos }} +# - name: Create fastlane whatsNew hash in environment +# id: buildinfo +# env: +# CHANGELOG_IOS: ${{ steps.releasenotes.outputs.notes_ios }} +# CHANGELOG_MACOS: ${{ steps.releasenotes.outputs.notes_macos }} +# run: | +# get_changelog() { +# local escaped=$(printf '%s\n' "$1" | jq -sRr @json) +# local json="{\"default\": {\"whats_new\": $escaped}," +# # for dir in ./appstore_metadata/*/; do +# # dir="$(basename "$dir")" +# # if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then +# # json="$json\"${dir%/}\": {\"whats_new\": $escaped}," +# # fi +# # done +# json="${json%,}}" +# echo "$json" +# } +# echo "buildinfo_ios<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" +# echo "$(get_changelog "$CHANGELOG_IOS")" | tee /dev/stderr >> "$GITHUB_OUTPUT" +# echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" +# +# echo "buildinfo_macos<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" +# echo "$(get_changelog "$CHANGELOG_MACOS")" | tee /dev/stderr >> "$GITHUB_OUTPUT" +# echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + - name: Create fastlane localized_app_info hash in environment + id: appinfo run: | - get_changelog() { - local escaped=$(printf '%s\n' "$1" | jq -sRr @json) - local json="{\"default\":{\"whats_new\":$escaped}," - for dir in ./appstore_metadata/*/; do - dir="$(basename "$dir")" - if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then - json="$json\"${dir%/}\":{\"whats_new\":$escaped}," - fi - done - json="${json%,}}" + build_appinfo_entry() { + local escaped_marketing_url=$(cat ./appstore_metadata/en-US/marketing_url.txt | jq -sRr @json) + local escaped_privacy_policy_url=$(cat ./appstore_metadata/en-US/privacy_url.txt | jq -sRr @json) + local json="{\"feedback_email\": \"info@monal-im.org\", \"marketing_url\": $escaped_marketing_url, \"privacy_policy_url\": $escaped_privacy_policy_url, \"description\": \"\"}" echo "$json" } - echo "buildinfo_ios<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - echo "$(get_changelog "$CHANGELOG_IOS")" | tee /dev/stderr >> "$GITHUB_OUTPUT" - echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - echo "buildinfo_macos<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - echo "$(get_changelog "$CHANGELOG_MACOS")" | tee /dev/stderr >> "$GITHUB_OUTPUT" + json="{" + json="$json\"default\": $(build_appinfo_entry)," + for dir in ./appstore_metadata/*/; do + dir="$(basename "$dir")" + if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then + json="$json\"${dir%/}\": $(build_appinfo_entry)," + fi + done + json="${json%,}}" + + echo "appinfo<<__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" + echo "$json" | tee /dev/stderr >> "$GITHUB_OUTPUT" echo "__EOF__" | tee /dev/stderr >> "$GITHUB_OUTPUT" - name: Publish ios to appstore connect #run: xcrun altool --upload-app -f ./Monal/build/ipa/Monal.ipa --type ios --asc-provider S8D843U34Y --team-id S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" env: - PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_ios }} + #PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_ios }} + PILOT_LOCALIZED_APP_INFO: ${{ steps.appinfo.outputs.appinfo }} + PILOT_CHANGELOG: ${{ steps.releasenotes.outputs.notes_ios }} run: | - fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" distribute_external:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true + fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" ipa:"./Monal/build/ipa/Monal.ipa" distribute_external:true notify_external_testers:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true - name: Notarize catalyst run: xcrun notarytool submit ./Monal/build/app/Monal.zip --wait --team-id S8D843U34Y --key "/Users/ci/appstoreconnect/apiKey.p8" --key-id "$(cat /Users/ci/appstoreconnect/apiKeyId.txt)" --issuer "$(cat /Users/ci/appstoreconnect/apiIssuerId.txt)" - name: staple @@ -181,9 +206,11 @@ jobs: - name: Publish catalyst to appstore connect #run: xcrun altool --upload-app --file ./Monal/build/app/Monal.pkg --type macos --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" --primary-bundle-id org.monal-im.prod.catalyst.monal env: - PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_macos }} + #PILOT_LOCALIZED_BUILD_INFO: ${{ steps.buildinfo.outputs.buildinfo_macos }} + PILOT_LOCALIZED_APP_INFO: ${{ steps.appinfo.outputs.appinfo }} + PILOT_CHANGELOG: ${{ steps.releasenotes.outputs.notes_macos }} run: | - fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" pkg:"./Monal/build/app/Monal.pkg" distribute_external:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true + fastlane run upload_to_testflight api_key_path:"/Users/ci/appstoreconnect/key.json" team_id:"S8D843U34Y" pkg:"./Monal/build/app/Monal.pkg" distribute_external:true notify_external_testers:true groups:"Internal Pre-Beta Testers","Public Beta" reject_build_waiting_for_review:true submit_beta_review:true - name: Release uses: softprops/action-gh-release@v2 with: diff --git a/.github/workflows/quicksy.build-push.yml b/.github/workflows/quicksy.build-push.yml index e86eac9b0..d75aa8a77 100644 --- a/.github/workflows/quicksy.build-push.yml +++ b/.github/workflows/quicksy.build-push.yml @@ -35,7 +35,7 @@ jobs: run: git submodule update -f --init --remote - name: Get last build tag and increment it run: | - oldBuildNumber=$(git tag --sort="v:refname" |grep "Quicksy_Build_iOS" | tail -n1 | sed 's/Quicksy_Build_iOS_//g') + oldBuildNumber=$(git tag --sort="v:refname" | grep "Quicksy_Build_iOS" | tail -n1 | sed 's/Quicksy_Build_iOS_//g') buildNumber=$(expr $oldBuildNumber + 1) echo "New buildNumber is $buildNumber" git tag Quicksy_Build_iOS_$buildNumber @@ -58,15 +58,16 @@ jobs: printf("\n") }' } - buildNumber="$(git tag --sort="v:refname" | grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g')" + buildNumber="$(git tag --sort="v:refname" | grep "Quicksy_Build_iOS" | tail -n1 | sed 's/Quicksy_Build_iOS_//g')" version="$(git log -n 1 --merges --pretty=format:%s | sed -E 's/^[\t\n ]*([^\n\t ]+)[\t\n ]+\(([^\n\t ]+)\)[\t\n ]*$/\1/g')" + version="6.4.2" mkdir -p /Users/ci/releases OUTPUT_FILE="/Users/ci/releases/$buildNumber.output" touch "$OUTPUT_FILE" echo "OUTPUT_FILE=$OUTPUT_FILE" | tee /dev/stderr >> "$GITHUB_OUTPUT" echo "buildNumber=$buildNumber" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "tag=Build_iOS_$buildNumber" | tee /dev/stderr >> "$OUTPUT_FILE" + echo "tag=Quicksy_Build_iOS_$buildNumber" | tee /dev/stderr >> "$OUTPUT_FILE" echo "version=$version" | tee /dev/stderr >> "$OUTPUT_FILE" echo "buildVersion=$(echo "$version" | grep -oE '^[0-9]+(\.[0-9]+){0,2}')" | tee /dev/stderr >> "$OUTPUT_FILE" @@ -114,7 +115,7 @@ jobs: - name: push tag to stable repo run: | buildNumber=$(git tag --sort="v:refname" | grep "Quicksy_Build_iOS" | tail -n1 | sed 's/Quicksy_Build_iOS_//g') - git push origin Build_iOS_$buildNumber + git push origin Quicksy_Build_iOS_$buildNumber - name: Create fastlane metadata directory id: metadata env: diff --git a/.github/workflows/stable.build-push.yml b/.github/workflows/stable.build-push.yml index 4dfef89db..0c82d9f66 100644 --- a/.github/workflows/stable.build-push.yml +++ b/.github/workflows/stable.build-push.yml @@ -37,7 +37,7 @@ jobs: run: git submodule update -f --init --remote - name: Get last build tag and increment it run: | - oldBuildNumber=$(git tag --sort="v:refname" |grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') + oldBuildNumber=$(git tag --sort="v:refname" | grep "Build_iOS" | grep -v "Quicksy_Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') buildNumber=$(expr $oldBuildNumber + 1) echo "New buildNumber is $buildNumber" git tag Build_iOS_$buildNumber @@ -60,7 +60,7 @@ jobs: printf("\n") }' } - buildNumber="$(git tag --sort="v:refname" | grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g')" + buildNumber="$(git tag --sort="v:refname" | grep "Build_iOS" | grep -v "Quicksy_Build_iOS" | tail -n1 | sed 's/Build_iOS_//g')" version="$(git log -n 1 --merges --pretty=format:%s | sed -E 's/^[\t\n ]*([^\n\t ]+)[\t\n ]+\(([^\n\t ]+)\)[\t\n ]*$/\1/g')" mkdir -p /Users/ci/releases OUTPUT_FILE="/Users/ci/releases/$buildNumber.output" @@ -120,7 +120,7 @@ jobs: run: xcrun altool --validate-app --file ./Monal/build/ipa/Monal.ipa --type ios -u $(cat /Users/ci/apple_connect_upload_mail.txt) -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" - name: push tag to stable repo run: | - buildNumber=$(git tag --sort="v:refname" |grep "Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') + buildNumber=$(git tag --sort="v:refname" |grep "Build_iOS" | grep -v "Quicksy_Build_iOS" | tail -n1 | sed 's/Build_iOS_//g') git push origin Build_iOS_$buildNumber - name: Create fastlane metadata directory id: metadata From 62ad8047b7d48d1306b66c9b65c6bea2af4573c0 Mon Sep 17 00:00:00 2001 From: Friedrich Altheide <11352905+FriedrichAltheide@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:47:44 +0200 Subject: [PATCH 30/38] rust: bump crates --- rust/Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 84a8ba227..61d28dbfa 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -146,22 +146,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.72", ] [[package]] @@ -222,9 +222,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6b6a2fb3a985e99cebfaefa9faa3024743da73304ca1c683a36429613d3d22" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] From b438e49c998446d1d09bd87efcc6010022e96988 Mon Sep 17 00:00:00 2001 From: Friedrich Altheide <11352905+FriedrichAltheide@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:12:29 +0200 Subject: [PATCH 31/38] bump webrtc-lib to 127.0.0 --- Monal/Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Monal/Podfile.lock b/Monal/Podfile.lock index 280ccdfe7..2fdcd6139 100644 --- a/Monal/Podfile.lock +++ b/Monal/Podfile.lock @@ -62,7 +62,7 @@ PODS: - "sqlite3/common (3.46.0+1)" - "sqlite3/perf-threadsafe (3.46.0+1)": - sqlite3/common - - WebRTC-lib (126.0.0) + - WebRTC-lib (127.0.0) DEPENDENCIES: - ASN1Decoder @@ -133,7 +133,7 @@ SPEC CHECKSUMS: SignalProtocolObjC: 1beb46b1d35733e7ab96a919f88bac20ec771c73 SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25 sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 - WebRTC-lib: 7e2e15d90ebca6e08a1eb5d4afc365d70e4b95b0 + WebRTC-lib: 3e56b5c5fe21b0d6b682e543c39f68c81ccc8f26 PODFILE CHECKSUM: e66b7bd410467c69adaa2ca9b0fca4fb025d8aa4 From 9cee6411ed9c415a65d95a43ad4e71eb88b9f2ed Mon Sep 17 00:00:00 2001 From: Friedrich Altheide <11352905+FriedrichAltheide@users.noreply.github.com> Date: Thu, 1 Aug 2024 19:49:54 +0200 Subject: [PATCH 32/38] rust: bump quick-xml --- rust/Cargo.lock | 4 ++-- rust/sdp-to-jingle/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 61d28dbfa..06c2b910e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.35.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e446ed58cef1bbfe847bc2fda0e2e4ea9f0e57b90c507d4781292590d72a4e" +checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" dependencies = [ "memchr", "serde", diff --git a/rust/sdp-to-jingle/Cargo.toml b/rust/sdp-to-jingle/Cargo.toml index 8ad2bef78..ce8568f91 100644 --- a/rust/sdp-to-jingle/Cargo.toml +++ b/rust/sdp-to-jingle/Cargo.toml @@ -11,6 +11,6 @@ crate-type = ["staticlib", "lib"] [dependencies] serde = {version = "1.0"} serde_derive = {version = "1.0"} -quick-xml = { version = "0.35.0", features = ["serialize", "overlapped-lists"] } +quick-xml = { version = "0.36", features = ["serialize", "overlapped-lists"] } webrtc-sdp = {version = "0.3.10", features = ["serialize"] } From 2cd398284a936f2701474bf993c6aaf1b6f1cc2b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 1 Aug 2024 14:23:13 +0200 Subject: [PATCH 33/38] Add a French translation of the appstore metadata --- appstore_metadata/fr-FR/description.txt | 20 ++++++++++++++++++++ appstore_metadata/fr-FR/keywords.txt | 1 + appstore_metadata/fr-FR/marketing_url.txt | 1 + appstore_metadata/fr-FR/privacy_url.txt | 1 + appstore_metadata/fr-FR/support_url.txt | 1 + 5 files changed, 24 insertions(+) create mode 100644 appstore_metadata/fr-FR/description.txt create mode 100644 appstore_metadata/fr-FR/keywords.txt create mode 100644 appstore_metadata/fr-FR/marketing_url.txt create mode 100644 appstore_metadata/fr-FR/privacy_url.txt create mode 100644 appstore_metadata/fr-FR/support_url.txt diff --git a/appstore_metadata/fr-FR/description.txt b/appstore_metadata/fr-FR/description.txt new file mode 100644 index 000000000..4698ba726 --- /dev/null +++ b/appstore_metadata/fr-FR/description.txt @@ -0,0 +1,20 @@ +C’est le meilleur moment pour se mettre à XMPP, un réseau de chat public libre que personne ne contrôle ou possède. Monal est une application facile à utiliser pour rejoindre le réseau XMPP. Téléchargez l’application, créez un compte, et ça y est vous pouvez chatter en quelques minutes. Elle est fonctionnellement identique aux applications de chat que vous connaissez, donc il n’y a aucun besoin d’« apprendre XMPP » ou même de se préoccuper de ce que c’est. + +Fonctionalités notables : +- Open Source +- Aucune pub ! Mettant l’accent sur la vie privée, Monal n’utilise aucune fonctionnalité de tracking. +- Ne lit aucune information personnelle. +- Avec une connexion directe à votre serveur, votre mot de passe et toutes vos autres informations ne sont jamais envoyées à un tiers. +- Chat chiffré avec OMEMO. +- Fonctionne avec les serveurs XMPP d’entreprises qui nécessitent un VPN. +- Chat multi-utilisateur·ice·s grâce à MUC. +- Appels audio/video. + +Implémente certaines extensions XMPP pour améliorer les communications mobiles : +- XEP-0357: Push Notifications, pour avoir des notifications même quand l’application est fermée. +- XEP-0280: Message Carbons, pour garder les messages synchronisés entre clients. +- XEP-0198: Stream Management, pour se reconnecter rapidement. +- XEP-0199: XMPP Ping, pour maintenir la connexion. +- XEP-0313: Message Archive Management, pour récupérer l’historique des messages. +- XEP-0352: Client State Indication, pour diminuer drastiquement la consommation énergétique. +- XEP-0363: HTTP File Upload, pour envoyer des images, des messages vocaux ou des fichiers dans les conversations. diff --git a/appstore_metadata/fr-FR/keywords.txt b/appstore_metadata/fr-FR/keywords.txt new file mode 100644 index 000000000..8bb582149 --- /dev/null +++ b/appstore_metadata/fr-FR/keywords.txt @@ -0,0 +1 @@ +xmpp, jabber, chat, messagerie instantanée, messages, ejabberd, prosody, OMEMO diff --git a/appstore_metadata/fr-FR/marketing_url.txt b/appstore_metadata/fr-FR/marketing_url.txt new file mode 100644 index 000000000..43952ebe2 --- /dev/null +++ b/appstore_metadata/fr-FR/marketing_url.txt @@ -0,0 +1 @@ +https://monal-im.org/ \ No newline at end of file diff --git a/appstore_metadata/fr-FR/privacy_url.txt b/appstore_metadata/fr-FR/privacy_url.txt new file mode 100644 index 000000000..4c9b027f0 --- /dev/null +++ b/appstore_metadata/fr-FR/privacy_url.txt @@ -0,0 +1 @@ +https://monal-im.org/privacy/ \ No newline at end of file diff --git a/appstore_metadata/fr-FR/support_url.txt b/appstore_metadata/fr-FR/support_url.txt new file mode 100644 index 000000000..1ffe2f0bb --- /dev/null +++ b/appstore_metadata/fr-FR/support_url.txt @@ -0,0 +1 @@ +https://monal-im.org/support/ \ No newline at end of file From d7e810cf7971066bce5ed584b2da4cca7669b6db Mon Sep 17 00:00:00 2001 From: Licaon_Kter Date: Thu, 1 Aug 2024 19:43:32 +0000 Subject: [PATCH 34/38] Appstore text - RO (#1174) --- appstore_metadata/ro-RO/description.txt | 20 ++++++++++++++++++++ appstore_metadata/ro-RO/keywords.txt | 1 + appstore_metadata/ro-RO/marketing_url.txt | 1 + appstore_metadata/ro-RO/privacy_url.txt | 1 + appstore_metadata/ro-RO/support_url.txt | 1 + 5 files changed, 24 insertions(+) create mode 100644 appstore_metadata/ro-RO/description.txt create mode 100644 appstore_metadata/ro-RO/keywords.txt create mode 100644 appstore_metadata/ro-RO/marketing_url.txt create mode 100644 appstore_metadata/ro-RO/privacy_url.txt create mode 100644 appstore_metadata/ro-RO/support_url.txt diff --git a/appstore_metadata/ro-RO/description.txt b/appstore_metadata/ro-RO/description.txt new file mode 100644 index 000000000..2292033df --- /dev/null +++ b/appstore_metadata/ro-RO/description.txt @@ -0,0 +1,20 @@ +Nu a existat niciodată un moment mai bun pentru a utiliza XMPP, o rețea publică gratuită de chat pe care nimeni nu o controlează și nu o deține. Monal este un mod rapid și prietenos de a utiliza XMPP. Trebuie doar să descărcați aplicația, să vă autentificați sau să vă înregistrați și sunteți gata de discuții în câteva minute. Arată și funcționează la fel ca alte aplicații, deci nu este nevoie să "învățați XMPP" sau chiar să vă pese ce este. + +Caracteristici notabile: +- Sursă deschisă +- Fără reclame! Accent puternic pe confidențialitate. Nu transmite date altora și nu vă analizează acțiunile +- Nu citește nicio informație personală +- Cu o conexiune directă la serverul dvs., parola dvs. și toate celelalte informații nu sunt niciodată trimise unei terțe părți +- Discuții criptate cu OMEMO +- Va funcționa cu servere XMPP corporatiste care necesită VPN +- Discuții de grup MUC +- Apeluri audio/video + +Implementează anumite extensii XMPP menite să îmbunătățească comunicarea mobilă. +- XEP-0357: Notificări push +- XEP-0280: Message Carbons menține mesajele sincronizate între clienți +- XEP-0198: Stream Management pentru reconectarea rapidă +- XEP-0199: Ping XMPP pentru menținerea conexiunilor +- XEP-0313: Message Archive Management pentru a descărca istoricul conversațiilor +- XEP-0352: Client State Indication pentru reducerea drastică a consumului de energie +- XEP-0363: HTTP File Upload pentru a trimite imagini în conversații diff --git a/appstore_metadata/ro-RO/keywords.txt b/appstore_metadata/ro-RO/keywords.txt new file mode 100644 index 000000000..db666557f --- /dev/null +++ b/appstore_metadata/ro-RO/keywords.txt @@ -0,0 +1 @@ +xmpp, jabber, discutie, mesagerie instantanee, mesagerie, ejabberd, prosody, OMEMO diff --git a/appstore_metadata/ro-RO/marketing_url.txt b/appstore_metadata/ro-RO/marketing_url.txt new file mode 100644 index 000000000..43952ebe2 --- /dev/null +++ b/appstore_metadata/ro-RO/marketing_url.txt @@ -0,0 +1 @@ +https://monal-im.org/ \ No newline at end of file diff --git a/appstore_metadata/ro-RO/privacy_url.txt b/appstore_metadata/ro-RO/privacy_url.txt new file mode 100644 index 000000000..4c9b027f0 --- /dev/null +++ b/appstore_metadata/ro-RO/privacy_url.txt @@ -0,0 +1 @@ +https://monal-im.org/privacy/ \ No newline at end of file diff --git a/appstore_metadata/ro-RO/support_url.txt b/appstore_metadata/ro-RO/support_url.txt new file mode 100644 index 000000000..1ffe2f0bb --- /dev/null +++ b/appstore_metadata/ro-RO/support_url.txt @@ -0,0 +1 @@ +https://monal-im.org/support/ \ No newline at end of file From 2090e5d3a0e6295583c3fbee966d72522764e677 Mon Sep 17 00:00:00 2001 From: Licaon_Kter Date: Thu, 1 Aug 2024 19:47:03 +0000 Subject: [PATCH 35/38] Description polish (#1175) Co-authored-by: Thilo Molitor --- appstore_metadata/en-US/description.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/appstore_metadata/en-US/description.txt b/appstore_metadata/en-US/description.txt index 36b113947..652c0fd7d 100644 --- a/appstore_metadata/en-US/description.txt +++ b/appstore_metadata/en-US/description.txt @@ -2,19 +2,19 @@ There has never been a better time to get into XMPP, a free public chat network Notable features: - Open Source -- No Ads! Strong focus on privacy. Does not phone home and does not have "metrics" software -- Does not read any personal information. -- With a direct connection to your server, your password and all other info is never sent to a third party. +- No Ads! Strong focus on privacy. Does not phone home and does not have software "metrics" +- Does not read any personal information +- With a direct connection to your server, your password and all other info are never sent to a third-party - OMEMO encrypted chat - Will work with corporate XMPP servers that require VPN -- MUC multi user chat +- Multi user chat (MUC) support for group chats - Audio/Video calls -Implements XMPP certain extensions intended to improve mobile communication. +Implements certain XMPP extensions intended to improve mobile communication: - XEP-0357: Push Notifications -- XEP-0280: Message Carbons keep messages in synch between clients. -- XEP-0198: Stream Management to quickly reconnect. -- XEP-0199: XMPP Ping to maintain connections. -- XEP-0313: Message Archive Management to download chat history. -- XEP-0352: Client State Indication for dramatic reduction on power use. -- XEP-0363: HTTP File Upload to send images in conversations. +- XEP-0280: Message Carbons keep messages in synch between clients +- XEP-0198: Stream Management to quickly reconnect +- XEP-0199: XMPP Ping to maintain connections +- XEP-0313: Message Archive Management to download chat history +- XEP-0352: Client State Indication for dramatic reduction on power use +- XEP-0363: HTTP File Upload to send images in conversations From a2920d4035b08e4af251b95766c35c1acce878e1 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 1 Aug 2024 23:41:41 +0200 Subject: [PATCH 36/38] Fix romanian language code See these links: https://github.com/fastlane/fastlane/issues/16625#issuecomment-658181756 https://github.com/fastlane/fastlane/issues/16625#issue-640591002 --- appstore_metadata/{ro-RO => ro}/description.txt | 0 appstore_metadata/{ro-RO => ro}/keywords.txt | 0 appstore_metadata/{ro-RO => ro}/marketing_url.txt | 0 appstore_metadata/{ro-RO => ro}/privacy_url.txt | 0 appstore_metadata/{ro-RO => ro}/support_url.txt | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename appstore_metadata/{ro-RO => ro}/description.txt (100%) rename appstore_metadata/{ro-RO => ro}/keywords.txt (100%) rename appstore_metadata/{ro-RO => ro}/marketing_url.txt (100%) rename appstore_metadata/{ro-RO => ro}/privacy_url.txt (100%) rename appstore_metadata/{ro-RO => ro}/support_url.txt (100%) diff --git a/appstore_metadata/ro-RO/description.txt b/appstore_metadata/ro/description.txt similarity index 100% rename from appstore_metadata/ro-RO/description.txt rename to appstore_metadata/ro/description.txt diff --git a/appstore_metadata/ro-RO/keywords.txt b/appstore_metadata/ro/keywords.txt similarity index 100% rename from appstore_metadata/ro-RO/keywords.txt rename to appstore_metadata/ro/keywords.txt diff --git a/appstore_metadata/ro-RO/marketing_url.txt b/appstore_metadata/ro/marketing_url.txt similarity index 100% rename from appstore_metadata/ro-RO/marketing_url.txt rename to appstore_metadata/ro/marketing_url.txt diff --git a/appstore_metadata/ro-RO/privacy_url.txt b/appstore_metadata/ro/privacy_url.txt similarity index 100% rename from appstore_metadata/ro-RO/privacy_url.txt rename to appstore_metadata/ro/privacy_url.txt diff --git a/appstore_metadata/ro-RO/support_url.txt b/appstore_metadata/ro/support_url.txt similarity index 100% rename from appstore_metadata/ro-RO/support_url.txt rename to appstore_metadata/ro/support_url.txt From d5d2bc82c47f175d0061491414d031b41e80500d Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 1 Aug 2024 23:53:04 +0200 Subject: [PATCH 37/38] Use localized appstore description as beta decription, too --- .github/workflows/beta.build-push.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 2f059537c..30132cbc2 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -155,9 +155,10 @@ jobs: id: appinfo run: | build_appinfo_entry() { - local escaped_marketing_url=$(cat ./appstore_metadata/en-US/marketing_url.txt | jq -sRr @json) - local escaped_privacy_policy_url=$(cat ./appstore_metadata/en-US/privacy_url.txt | jq -sRr @json) - local json="{\"feedback_email\": \"info@monal-im.org\", \"marketing_url\": $escaped_marketing_url, \"privacy_policy_url\": $escaped_privacy_policy_url, \"description\": \"\"}" + local escaped_marketing_url=$(cat ./appstore_metadata/$1/marketing_url.txt | jq -sRr @json) + local escaped_privacy_policy_url=$(cat ./appstore_metadata/$1/privacy_url.txt | jq -sRr @json) + local escaped_description=$(cat ./appstore_metadata/$1/description.txt | jq -sRr @json) + local json="{\"feedback_email\": \"info@monal-im.org\", \"marketing_url\": $escaped_marketing_url, \"privacy_policy_url\": $escaped_privacy_policy_url, \"description\": $escaped_description}" echo "$json" } @@ -166,7 +167,7 @@ jobs: for dir in ./appstore_metadata/*/; do dir="$(basename "$dir")" if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then - json="$json\"${dir%/}\": $(build_appinfo_entry)," + json="$json\"${dir%/}\": $(build_appinfo_entry "${dir%/}")," fi done json="${json%,}}" From 4a7132d0c3ad08674d7094cd57bdb47b8041c573 Mon Sep 17 00:00:00 2001 From: Thilo Molitor Date: Thu, 1 Aug 2024 23:59:46 +0200 Subject: [PATCH 38/38] Allow language codes without hyphen --- .github/workflows/beta.build-push.yml | 4 ++-- .github/workflows/quicksy.build-push.yml | 2 +- .github/workflows/stable.build-push.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index 30132cbc2..467024a64 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -137,7 +137,7 @@ jobs: # local json="{\"default\": {\"whats_new\": $escaped}," # # for dir in ./appstore_metadata/*/; do # # dir="$(basename "$dir")" -# # if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then +# # if [[ -d "./appstore_metadata/$dir" ]]; then # # json="$json\"${dir%/}\": {\"whats_new\": $escaped}," # # fi # # done @@ -166,7 +166,7 @@ jobs: json="$json\"default\": $(build_appinfo_entry)," for dir in ./appstore_metadata/*/; do dir="$(basename "$dir")" - if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then + if [[ -d "./appstore_metadata/$dir" ]]; then json="$json\"${dir%/}\": $(build_appinfo_entry "${dir%/}")," fi done diff --git a/.github/workflows/quicksy.build-push.yml b/.github/workflows/quicksy.build-push.yml index d75aa8a77..3342dee47 100644 --- a/.github/workflows/quicksy.build-push.yml +++ b/.github/workflows/quicksy.build-push.yml @@ -126,7 +126,7 @@ jobs: echo -n "$(date +%Y) Thilo Molitor" > "$path_ios/copyright.txt" for dir in ./appstore_quicksy_metadata/*/; do dir="$(basename "$dir")" - if [[ -d "./appstore_quicksy_metadata/$dir" && "$dir" == *-* ]]; then + if [[ -d "./appstore_quicksy_metadata/$dir" ]]; then echo -n "$CHANGELOG_IOS" > "$path_ios/${dir%/}/release_notes.txt" fi done diff --git a/.github/workflows/stable.build-push.yml b/.github/workflows/stable.build-push.yml index 0c82d9f66..bba4f8c03 100644 --- a/.github/workflows/stable.build-push.yml +++ b/.github/workflows/stable.build-push.yml @@ -133,7 +133,7 @@ jobs: echo -n "$(date +%Y) Thilo Molitor" > "$path_ios/copyright.txt" for dir in ./appstore_metadata/*/; do dir="$(basename "$dir")" - if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then + if [[ -d "./appstore_metadata/$dir" ]]; then echo -n "$CHANGELOG_IOS" > "$path_ios/${dir%/}/release_notes.txt" fi done @@ -144,7 +144,7 @@ jobs: echo -n "$(date +%Y) Thilo Molitor" > "$path_macos/copyright.txt" for dir in ./appstore_metadata/*/; do dir="$(basename "$dir")" - if [[ -d "./appstore_metadata/$dir" && "$dir" == *-* ]]; then + if [[ -d "./appstore_metadata/$dir" ]]; then echo -n "$CHANGELOG_MACOS" > "$path_macos/${dir%/}/release_notes.txt" fi done