diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index 374c82bdef..905c365582 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -15,7 +15,7 @@ env:
jobs:
build:
name: Build
- runs-on: macos-12
+ runs-on: macos-14
# Concurrency group not needed as this workflow only runs on develop which we always want to test.
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index ba69c463e3..5ffde41ac8 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -16,7 +16,7 @@ env:
jobs:
tests:
name: Tests
- runs-on: macos-12
+ runs-on: macos-14
concurrency:
# When running on develop, use the sha to allow all runs of this workflow to run concurrently.
diff --git a/.github/workflows/ci-ui-tests.yml b/.github/workflows/ci-ui-tests.yml
new file mode 100644
index 0000000000..b13272947f
--- /dev/null
+++ b/.github/workflows/ci-ui-tests.yml
@@ -0,0 +1,63 @@
+name: UI Tests CI
+
+on:
+ pull_request:
+
+ workflow_dispatch:
+
+env:
+ # Make the git branch for a PR available to our Fastfile
+ MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }}
+
+jobs:
+ tests:
+ name: UI Tests
+ runs-on: macos-14
+
+ concurrency:
+ # Only allow a single run of this workflow on each branch, automatically cancelling older runs.
+ group: ui-tests-${{ github.head_ref }}
+ cancel-in-progress: true
+
+ steps:
+ - uses: actions/checkout@v2
+
+ # Common cache
+ # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job
+ - uses: actions/cache@v2
+ with:
+ path: Pods
+ key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-pods-
+ - uses: actions/cache@v2
+ with:
+ path: vendor/bundle
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-gems-
+
+ # Make sure we use the latest version of MatrixSDK
+ - name: Reset MatrixSDK pod
+ run: rm -rf Pods/MatrixSDK
+
+ # Common setup
+ # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job
+ - name: Brew bundle
+ run: brew bundle
+ - name: Bundle install
+ run: |
+ bundle config path vendor/bundle
+ bundle install --jobs 4 --retry 3
+ - name: Use right MatrixSDK versions
+ run: bundle exec fastlane point_dependencies_to_related_branches
+
+ # Main step
+ - name: UI tests
+ run: bundle exec fastlane uitest
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v3
+ with:
+ flags: uitests
+
\ No newline at end of file
diff --git a/.github/workflows/release-alpha.yml b/.github/workflows/release-alpha.yml
new file mode 100644
index 0000000000..e610628b45
--- /dev/null
+++ b/.github/workflows/release-alpha.yml
@@ -0,0 +1,95 @@
+name: Build alpha release
+
+on:
+
+ # Triggers the workflow on any pull request
+ pull_request:
+ types: [ labeled, synchronize, opened, reopened ]
+
+env:
+ # Make the git branch for a PR available to our Fastfile
+ MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }}
+
+jobs:
+ build:
+ # Only run for PRs that contain the trigger label. The action will fail for forks due to
+ # missing secrets, but there's no need to handle this as it won't run automatically.
+ if: contains(github.event.pull_request.labels.*.name, 'Trigger-PR-Build')
+
+ name: Release
+ runs-on: macos-14
+
+ concurrency:
+ # Only allow a single run of this workflow on each branch, automatically cancelling older runs.
+ group: alpha-${{ github.head_ref }}
+ cancel-in-progress: true
+
+ steps:
+ - uses: actions/checkout@v2
+
+ # Common cache
+ # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job
+ - name: Cache CocoaPods libraries
+ uses: actions/cache@v2
+ with:
+ path: Pods
+ key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-pods-
+
+ - name: Cache Ruby gems
+ uses: actions/cache@v2
+ with:
+ path: vendor/bundle
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-gems-
+
+ # Make sure we use the latest version of MatrixSDK
+ - name: Reset MatrixSDK pod
+ run: rm -rf Pods/MatrixSDK
+
+ # Common setup
+ # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job
+ - name: Brew bundle
+ run: brew bundle
+ - name: Bundle install
+ run: |
+ bundle config path vendor/bundle
+ bundle install --jobs 4 --retry 3
+ - name: Use right MatrixSDK versions
+ run: bundle exec fastlane point_dependencies_to_related_branches
+
+ # Import alpha release private signing certificate
+ - name: Import signing certificate
+ uses: apple-actions/import-codesign-certs@v1
+ with:
+ p12-file-base64: ${{ secrets.ALPHA_CERTIFICATES_P12 }}
+ p12-password: ${{ secrets.ALPHA_CERTIFICATES_P12_PASSWORD }}
+
+ # Main step
+ # The Ad-hoc release link will be referenced as 'DIAWI_FILE_LINK'
+ # and QR link as 'DIAWI_QR_CODE_LINK' when the Diawi upload succeed
+ - name: Build Ad-hoc release and send it to Diawi
+ run: bundle exec fastlane alpha
+ env:
+ APPSTORECONNECT_KEY_ID: ${{ secrets.APPSTORECONNECT_KEY_ID }}
+ APPSTORECONNECT_KEY_ISSUER_ID: ${{ secrets.APPSTORECONNECT_KEY_ISSUER_ID }}
+ APPSTORECONNECT_KEY_CONTENT: ${{ secrets.APPSTORECONNECT_KEY_CONTENT }}
+ DIAWI_API_TOKEN: ${{ secrets.DIAWI_API_TOKEN }}
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+
+ - name: Add or update PR comment with Ad-hoc release informations
+ uses: NejcZdovc/comment-pr@v1
+ with:
+ message: |
+ :iphone: Scan the QR code below to install the build for this PR.
+ :lock: This build is for internal testing purpose. Only devices listed in the ad-hoc provisioning profile can install Element Alpha.
+
+ 
+
+ If you can't scan the QR code you can install the build via this link: ${{ env.DIAWI_FILE_LINK }}
+ # Enables to identify and update existing Ad-hoc release message on new commit in the PR
+ identifier: "GITHUB_COMMENT_ADHOC_RELEASE"
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
new file mode 100644
index 0000000000..d902ca1368
--- /dev/null
+++ b/.github/workflows/sonarcloud.yml
@@ -0,0 +1,31 @@
+name: SonarCloud analysis
+
+on:
+ push:
+ branches: [ "develop" ]
+ pull_request:
+ branches: [ "develop" ]
+ workflow_dispatch:
+
+permissions:
+ pull-requests: read # allows SonarCloud to decorate PRs with analysis results
+
+jobs:
+ Analysis:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Analyze with SonarCloud
+
+ # You can pin the exact commit or the version.
+ uses: SonarSource/sonarcloud-github-action@master
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate the token on Sonarcloud.io, add it to the secrets of this repo
+ with:
+ # Additional arguments for the sonarcloud scanner
+ args:
+ -Dsonar.projectKey=vector-im_element-ios
+ -Dsonar.organization=new_vector_ltd_organization
+ -Dsonar.inclusions=RiotSwiftUI/**
+ # For more info about the parameters, please refer to https://docs.sonarcloud.io/advanced-setup/analysis-parameters/
\ No newline at end of file
diff --git a/.github/workflows/triage-incoming.yml b/.github/workflows/triage-incoming.yml
deleted file mode 100644
index 11f3280cc9..0000000000
--- a/.github/workflows/triage-incoming.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Move new issues onto Issue triage board
-
-on:
- issues:
- types: [opened]
-
-jobs:
- automate-project-columns:
- runs-on: ubuntu-latest
- steps:
- - uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
- with:
- project: Issue triage
- column: Incoming
- repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-
- add_to_triage:
- runs-on: ubuntu-latest
- if: >
- github.repository == 'element-hq/element-ios'
- steps:
- - uses: octokit/graphql-action@v2.x
- with:
- headers: '{"GraphQL-Features": "projects_next_graphql"}'
- query: |
- mutation add_to_project($projectid:ID!,$contentid:ID!) {
- addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
- item {
- id
- }
- }
- }
- projectid: ${{ env.PROJECT_ID }}
- contentid: ${{ github.event.issue.node_id }}
- env:
- PROJECT_ID: "PVT_kwDOAM0swc4AMlHr"
- GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/triage-move-labelled.yml b/.github/workflows/triage-move-labelled.yml
index 4282571424..b14355db80 100644
--- a/.github/workflows/triage-move-labelled.yml
+++ b/.github/workflows/triage-move-labelled.yml
@@ -30,17 +30,6 @@ jobs:
labels: ['Z-Labs']
})
- move_needs_info_issues:
- name: X-Needs-Info issues to Need info column on triage board
- runs-on: ubuntu-latest
- steps:
- - uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338
- with:
- action-token: "${{ secrets.ELEMENT_BOT_TOKEN }}"
- project-url: "https://github.com/element-hq/element-ios/projects/12"
- column-name: "Need info"
- label-name: "X-Needs-Info"
-
add_priority_design_issues_to_project:
name: P1 X-Needs-Design to Design project board
runs-on: ubuntu-latest
@@ -68,64 +57,3 @@ jobs:
with:
project-url: https://github.com/orgs/element-hq/projects/28
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-
- ex_plorers:
- name: Add labelled issues to X-Plorer project
- runs-on: ubuntu-latest
- if: >
- contains(github.event.issue.labels.*.name, 'Team: Element X Feature')
- steps:
- - uses: actions/add-to-project@main
- with:
- project-url: https://github.com/orgs/element-hq/projects/73
- github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-
- ps_features1:
- name: Add labelled issues to PS features team 1
- runs-on: ubuntu-latest
- if: >
- contains(github.event.issue.labels.*.name, 'A-Polls') ||
- contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
- (contains(github.event.issue.labels.*.name, 'A-Voice-Messages') &&
- !contains(github.event.issue.labels.*.name, 'A-Broadcast')) ||
- (contains(github.event.issue.labels.*.name, 'A-Session-Mgmt') &&
- contains(github.event.issue.labels.*.name, 'A-User-Settings'))
- steps:
- - uses: actions/add-to-project@main
- with:
- project-url: https://github.com/orgs/element-hq/projects/56
- github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-
- ps_features2:
- name: Add labelled issues to PS features team 2
- runs-on: ubuntu-latest
- if: >
- contains(github.event.issue.labels.*.name, 'A-DM-Start') ||
- contains(github.event.issue.labels.*.name, 'A-Broadcast')
- steps:
- - uses: actions/add-to-project@main
- with:
- project-url: https://github.com/orgs/element-hq/projects/58
- github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-
- ps_features3:
- name: Add labelled issues to PS features team 3
- runs-on: ubuntu-latest
- if: >
- contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
- steps:
- - uses: actions/add-to-project@main
- with:
- project-url: https://github.com/orgs/element-hq/projects/57
- github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-
- voip:
- name: Add labelled issues to VoIP project board
- runs-on: ubuntu-latest
- if: >
- contains(github.event.issue.labels.*.name, 'Team: VoIP')
- steps:
- - uses: actions/add-to-project@main
- with:
- project-url: https://github.com/orgs/element-hq/projects/41
- github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/BroadcastUploadExtension/SupportingFiles/PrivacyInfo.xcprivacy b/BroadcastUploadExtension/SupportingFiles/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..500ae9affe
--- /dev/null
+++ b/BroadcastUploadExtension/SupportingFiles/PrivacyInfo.xcprivacy
@@ -0,0 +1,41 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ 1C8F.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ 7D9E.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 3D61.1
+
+
+
+
+
diff --git a/Btchap/Config/BuildSettings.swift b/Btchap/Config/BuildSettings.swift
index f577db02a8..32ddc2f784 100644
--- a/Btchap/Config/BuildSettings.swift
+++ b/Btchap/Config/BuildSettings.swift
@@ -237,6 +237,7 @@ final class BuildSettings: NSObject {
static let tchapFeatureNotificationByEmail = "tchapFeatureNotificationByEmail"
static let tchapFeatureVoiceOverIP = "tchapFeatureVoiceOverIP"
static let tchapFeatureVideoOverIP = "tchapFeatureVideoOverIP" // Tchap: in pre-prod, allow any feature to any instance.
+ static let tchapFeatureGeolocationSharing = "tchapFeatureGeolocationSharing" // linked to `locationSharingEnabled` property (see above)
static var tchapFeaturesAllowedHomeServersForFeature: [String: [String]] = [
tchapFeatureAnyFeature: [ tchapFeatureAnyHomeServer ]
]
@@ -413,9 +414,14 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
/// Overwritten by the home server's .well-known configuration (if any exists)
- static let defaultTileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=")!
-
- static let locationSharingEnabled = false // Currently disabled in Tchap.
+ // Tchap: handle different map providers.
+ private enum TchapMapProvider: String {
+ case geoDataGouv = "https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
+ case ign = "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json"
+ }
+ static let defaultTileServerMapStyleURL = URL(string: TchapMapProvider.geoDataGouv.rawValue)!
+
+ static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
diff --git a/Btchap/Generated/InfoPlist.swift b/Btchap/Generated/InfoPlist.swift
index d65235e8cb..fd4913b7ce 100644
--- a/Btchap/Generated/InfoPlist.swift
+++ b/Btchap/Generated/InfoPlist.swift
@@ -30,6 +30,8 @@ internal enum InfoPlist {
internal static let nsCameraUsageDescription: String = _document["NSCameraUsageDescription"]
internal static let nsContactsUsageDescription: String = _document["NSContactsUsageDescription"]
internal static let nsFaceIDUsageDescription: String = _document["NSFaceIDUsageDescription"]
+ internal static let nsLocationAlwaysAndWhenInUseUsageDescription: String = _document["NSLocationAlwaysAndWhenInUseUsageDescription"]
+ internal static let nsLocationWhenInUseUsageDescription: String = _document["NSLocationWhenInUseUsageDescription"]
internal static let nsMicrophoneUsageDescription: String = _document["NSMicrophoneUsageDescription"]
internal static let nsPhotoLibraryUsageDescription: String = _document["NSPhotoLibraryUsageDescription"]
internal static let uiBackgroundModes: [String] = _document["UIBackgroundModes"]
diff --git a/Btchap/SupportingFiles/Info.plist b/Btchap/SupportingFiles/Info.plist
index ec38ef38e4..5e06e305b9 100644
--- a/Btchap/SupportingFiles/Info.plist
+++ b/Btchap/SupportingFiles/Info.plist
@@ -53,12 +53,17 @@
In order to show who among your contacts already uses Tchap, we can exploit the e-mail addresses of your address book. These data will not be stored. For more information, please visit the privacy policy page in the app settings
NSFaceIDUsageDescription
Face ID is used to access your app.
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ When you share your location to people, Tchap needs access to show them a map.
+ NSLocationWhenInUseUsageDescription
+ When you share your location to people, Tchap needs access to show them a map.
NSMicrophoneUsageDescription
Tchap needs to access your microphone to take videos, and record voice messages.
NSPhotoLibraryUsageDescription
This allows you to select pictures or videos from the photo library, and send them in your conversations. You can also use one of these pictures to set your profile picture.
UIBackgroundModes
+ location
remote-notification
voip
diff --git a/CHANGES.md b/CHANGES.md
index b2a673e097..eb5267b66d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,35 @@
+## Changes in 1.11.9 (2024-04-02)
+
+Others
+
+- Update matrix-analytics-events to version 0.15.0 ([#7768](https://github.com/element-hq/element-ios/pull/7768))
+- Upgrade to build with Xcode 15.2
+- Add a privacy manifest
+
+
+## Changes in 1.11.8 (2024-03-05)
+
+đ Improvements
+
+- Disable the mark as unread feature to avoid it clashing with the new MSC2876 based one ([#7758](https://github.com/element-hq/element-ios/pull/7758))
+
+đ Bugfixes
+
+- Fix a bug where QR codes aren't detected if the camera is too close. ([#7762](https://github.com/element-hq/element-ios/pull/7762))
+- Fix dictation when using the Rich Text Editor ([#7752](https://github.com/element-hq/element-ios/issues/7752))
+
+
+## Changes in 1.11.7 (2024-02-07)
+
+đ Improvements
+
+- Upgrade MatrixSDK version ([v0.27.6](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.27.6)).
+
+đ Bugfixes
+
+- Fix swapped accessibility label between strikethrough and underline format buttons in RTE. ([#7743](https://github.com/element-hq/element-ios/pull/7743))
+
+
## Changes in 1.11.6 (2024-01-09)
đ Improvements
diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift
index b73ee1f6a7..9abdd1f5f4 100644
--- a/Config/BuildSettings.swift
+++ b/Config/BuildSettings.swift
@@ -423,9 +423,14 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
/// Overwritten by the home server's .well-known configuration (if any exists)
- static let defaultTileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=")!
-
- static let locationSharingEnabled = false // Currently disabled in Tchap.
+ // Tchap: handle different map providers.
+ private enum TchapMapProvider: String {
+ case geoDataGouv = "https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
+ case ign = "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json"
+ }
+ static let defaultTileServerMapStyleURL = URL(string: TchapMapProvider.geoDataGouv.rawValue)!
+
+ static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
diff --git a/DevTchap/Config/BuildSettings.swift b/DevTchap/Config/BuildSettings.swift
index 58ee2009f4..08f236910a 100644
--- a/DevTchap/Config/BuildSettings.swift
+++ b/DevTchap/Config/BuildSettings.swift
@@ -238,6 +238,7 @@ final class BuildSettings: NSObject {
static let tchapFeatureNotificationByEmail = "tchapFeatureNotificationByEmail"
static let tchapFeatureVoiceOverIP = "tchapFeatureVoiceOverIP"
static let tchapFeatureVideoOverIP = "tchapFeatureVideoOverIP" // Tchap: in Dev, allow any feature to any instance.
+ static let tchapFeatureGeolocationSharing = "tchapFeatureGeolocationSharing" // linked to `locationSharingEnabled` property (see above)
static var tchapFeaturesAllowedHomeServersForFeature: [String: [String]] = [
tchapFeatureAnyFeature: [ tchapFeatureAnyHomeServer ]
]
@@ -414,9 +415,14 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
/// Overwritten by the home server's .well-known configuration (if any exists)
- static let defaultTileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=")!
-
- static let locationSharingEnabled = false // Currently disabled in Tchap.
+ // Tchap: handle different map providers.
+ private enum TchapMapProvider: String {
+ case geoDataGouv = "https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
+ case ign = "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json"
+ }
+ static let defaultTileServerMapStyleURL = URL(string: TchapMapProvider.geoDataGouv.rawValue)!
+
+ static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
diff --git a/DevTchap/Generated/InfoPlist.swift b/DevTchap/Generated/InfoPlist.swift
index c2b39bfa29..2306b5b8c2 100644
--- a/DevTchap/Generated/InfoPlist.swift
+++ b/DevTchap/Generated/InfoPlist.swift
@@ -29,6 +29,8 @@ internal enum InfoPlist {
internal static let nsCameraUsageDescription: String = _document["NSCameraUsageDescription"]
internal static let nsContactsUsageDescription: String = _document["NSContactsUsageDescription"]
internal static let nsFaceIDUsageDescription: String = _document["NSFaceIDUsageDescription"]
+ internal static let nsLocationAlwaysAndWhenInUseUsageDescription: String = _document["NSLocationAlwaysAndWhenInUseUsageDescription"]
+ internal static let nsLocationWhenInUseUsageDescription: String = _document["NSLocationWhenInUseUsageDescription"]
internal static let nsMicrophoneUsageDescription: String = _document["NSMicrophoneUsageDescription"]
internal static let nsPhotoLibraryUsageDescription: String = _document["NSPhotoLibraryUsageDescription"]
internal static let uiBackgroundModes: [String] = _document["UIBackgroundModes"]
diff --git a/DevTchap/SupportingFiles/Info.plist b/DevTchap/SupportingFiles/Info.plist
index cbd3ec180b..4303301c18 100644
--- a/DevTchap/SupportingFiles/Info.plist
+++ b/DevTchap/SupportingFiles/Info.plist
@@ -51,12 +51,17 @@
In order to show who among your contacts already uses Tchap, we can exploit the e-mail addresses of your address book. These data will not be stored. For more information, please visit the privacy policy page in the app settings
NSFaceIDUsageDescription
Face ID is used to access your app.
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ When you share your location to people, Tchap needs access to show them a map.
+ NSLocationWhenInUseUsageDescription
+ When you share your location to people, Tchap needs access to show them a map.
NSMicrophoneUsageDescription
Tchap needs to access your microphone to take videos, and record voice messages.
NSPhotoLibraryUsageDescription
This allows you to select pictures or videos from the photo library, and send them in your conversations. You can also use one of these pictures to set your profile picture.
UIBackgroundModes
+ location
remote-notification
voip
diff --git a/Gemfile.lock b/Gemfile.lock
index 02bb363639..519e1f087e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,9 +7,11 @@ GIT
GEM
remote: https://rubygems.org/
specs:
- CFPropertyList (3.0.6)
+ CFPropertyList (3.0.7)
+ base64
+ nkf
rexml
- activesupport (7.1.2)
+ activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -19,32 +21,32 @@ GEM
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
- addressable (2.8.5)
+ addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
- artifactory (3.0.15)
+ artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
- aws-partitions (1.859.0)
- aws-sdk-core (3.188.0)
- aws-eventstream (~> 1, >= 1.0.2)
+ aws-partitions (1.899.0)
+ aws-sdk-core (3.191.4)
+ aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
- aws-sigv4 (~> 1.5)
+ aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.73.0)
- aws-sdk-core (~> 3, >= 3.188.0)
+ aws-sdk-kms (1.78.0)
+ aws-sdk-core (~> 3, >= 3.191.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.140.0)
- aws-sdk-core (~> 3, >= 3.188.0)
+ aws-sdk-s3 (1.146.0)
+ aws-sdk-core (~> 3, >= 3.191.0)
aws-sdk-kms (~> 1)
- aws-sigv4 (~> 1.6)
- aws-sigv4 (1.7.0)
+ aws-sigv4 (~> 1.8)
+ aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
- bigdecimal (3.1.4)
+ bigdecimal (3.1.7)
claide (1.1.0)
clamp (1.3.2)
cocoapods (1.14.3)
@@ -88,20 +90,19 @@ GEM
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
- concurrent-ruby (1.2.2)
+ concurrent-ruby (1.2.3)
connection_pool (2.4.1)
declarative (0.0.20)
digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
- domain_name (0.6.20231109)
+ domain_name (0.6.20240107)
dotenv (2.8.1)
- drb (2.2.0)
- ruby2_keywords
+ drb (2.2.1)
emoji_regex (3.2.3)
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
- excon (0.104.0)
+ excon (0.110.0)
faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
@@ -130,8 +131,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
- fastimage (2.2.7)
- fastlane (2.217.0)
+ fastimage (2.3.0)
+ fastlane (2.219.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -150,6 +151,7 @@ GEM
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
http-cookie (~> 1.0.5)
@@ -158,7 +160,7 @@ GEM
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
- optparse (~> 0.1.1)
+ optparse (>= 0.1.1)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
@@ -172,7 +174,7 @@ GEM
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-brew (0.1.1)
- fastlane-plugin-sentry (1.16.0)
+ fastlane-plugin-sentry (1.20.0)
os (~> 1.1, >= 1.1.4)
fastlane-plugin-versioning (0.5.2)
fastlane-plugin-xcodegen (1.1.0)
@@ -181,9 +183,9 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.53.0)
+ google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
- google-apis-core (0.11.2)
+ google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
@@ -191,24 +193,23 @@ GEM
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
- webrick
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
- google-apis-storage_v1 (0.29.0)
+ google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
- google-cloud-core (1.6.0)
- google-cloud-env (~> 1.0)
+ google-cloud-core (1.7.0)
+ google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-cloud-errors (1.3.1)
- google-cloud-storage (1.45.0)
+ google-cloud-errors (1.4.0)
+ google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
- google-apis-storage_v1 (~> 0.29.0)
+ google-apis-storage_v1 (~> 0.31.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
@@ -222,29 +223,31 @@ GEM
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
- i18n (1.14.1)
+ i18n (1.14.4)
concurrent-ruby (~> 1.0)
jmespath (1.6.2)
- json (2.6.3)
- jwt (2.7.1)
+ json (2.7.1)
+ jwt (2.8.1)
+ base64
mini_magick (4.12.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
- minitest (5.20.0)
+ minitest (5.22.3)
molinillo (0.8.0)
multi_json (1.15.0)
- multipart-post (2.3.0)
+ multipart-post (2.4.0)
mutex_m (0.2.0)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
- nokogiri (1.15.5)
+ nkf (0.2.0)
+ nokogiri (1.15.6)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
- optparse (0.1.1)
+ optparse (0.4.0)
os (1.1.4)
- plist (3.7.0)
+ plist (3.7.1)
public_suffix (4.0.7)
racc (1.7.3)
rake (13.1.0)
@@ -259,7 +262,7 @@ GEM
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
- signet (0.18.0)
+ signet (0.19.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
@@ -278,7 +281,7 @@ GEM
unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
- tty-screen (0.8.1)
+ tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
typhoeus (1.4.1)
@@ -287,12 +290,11 @@ GEM
concurrent-ruby (~> 1.0)
uber (0.1.0)
unicode-display_width (2.5.0)
- webrick (1.8.1)
word_wrap (1.0.0)
xcode-install (2.8.1)
claide (>= 0.9.1)
fastlane (>= 2.1.0, < 3.0.0)
- xcodeproj (1.23.0)
+ xcodeproj (1.24.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
diff --git a/Podfile b/Podfile
index fc693dd78d..f662089b20 100644
--- a/Podfile
+++ b/Podfile
@@ -16,7 +16,7 @@ use_frameworks!
# - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => âŠ, :podspec => âŠ) for MatrixSDK repo. Used by Fastfile during CI
#
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
-$matrixSDKVersion = '= 0.27.5'
+$matrixSDKVersion = '= 0.27.6'
# $matrixSDKVersion = :local
# $matrixSDKVersion = { :branch => 'develop'}
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }
@@ -59,7 +59,7 @@ end
def import_SwiftUI_pods
pod 'Introspect', '~> 0.1'
pod 'DSBottomSheet', '~> 0.3'
- pod 'ZXingObjC', '~> 3.6.5'
+ pod 'ZXingObjC', '~> 3.6.9'
end
def import_Common_pods
diff --git a/Podfile.lock b/Podfile.lock
index 9944489d39..dd6cd62b25 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -39,9 +39,9 @@ PODS:
- LoggerAPI (1.9.200):
- Logging (~> 1.1)
- Logging (1.4.0)
- - MatrixSDK (0.27.5):
- - MatrixSDK/Core (= 0.27.5)
- - MatrixSDK/Core (0.27.5):
+ - MatrixSDK (0.27.6):
+ - MatrixSDK/Core (= 0.27.6)
+ - MatrixSDK/Core (0.27.6):
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
@@ -49,7 +49,7 @@ PODS:
- OLMKit (~> 3.2.5)
- Realm (= 10.27.0)
- SwiftyBeaver (= 1.9.5)
- - MatrixSDK/JingleCallStack (0.27.5):
+ - MatrixSDK/JingleCallStack (0.27.6):
- JitsiMeetSDKLite (= 8.1.2-lite)
- MatrixSDK/Core
- MatrixSDKCrypto (0.3.13)
@@ -87,9 +87,9 @@ PODS:
- UICollectionViewRightAlignedLayout (0.0.3)
- WeakDictionary (2.0.2)
- zxcvbn-ios (1.0.4)
- - ZXingObjC (3.6.5):
- - ZXingObjC/All (= 3.6.5)
- - ZXingObjC/All (3.6.5)
+ - ZXingObjC (3.6.9):
+ - ZXingObjC/All (= 3.6.9)
+ - ZXingObjC/All (3.6.9)
DEPENDENCIES:
- Down (~> 0.11.0)
@@ -102,8 +102,8 @@ DEPENDENCIES:
- KeychainAccess (~> 4.2.2)
- KTCenterFlowLayout (~> 1.3.1)
- libPhoneNumber-iOS (~> 0.9.13)
- - MatrixSDK (= 0.27.5)
- - MatrixSDK/JingleCallStack (= 0.27.5)
+ - MatrixSDK (= 0.27.6)
+ - MatrixSDK/JingleCallStack (= 0.27.6)
- OLMKit
- PostHog (~> 2.0.0)
- ReadMoreTextView (~> 3.0.1)
@@ -119,7 +119,7 @@ DEPENDENCIES:
- UICollectionViewRightAlignedLayout (~> 0.0.3)
- WeakDictionary (~> 2.0)
- zxcvbn-ios
- - ZXingObjC (~> 3.6.5)
+ - ZXingObjC (~> 3.6.9)
SPEC REPOS:
trunk:
@@ -187,7 +187,7 @@ SPEC CHECKSUMS:
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
- MatrixSDK: f92ffead50eda83c99786afefed9be739987f338
+ MatrixSDK: 4129ab9c0acda1d0aad50b1c9765bd795b8d70b9
MatrixSDKCrypto: bf08b72f2cd015d8749420a2b8b92fc0536bedf4
OLMKit: da115f16582e47626616874e20f7bb92222c7a51
PostHog: f9e5c13ceea86bb5314218c85d16125b797eb332
@@ -206,7 +206,7 @@ SPEC CHECKSUMS:
UICollectionViewRightAlignedLayout: 823eef8c567eba4a44c21bc2ffcb0d0d5f361e2d
WeakDictionary: 8cd038acd77e5d54ca4ebaec3d20853d732b45e0
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
- ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
+ ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
PODFILE CHECKSUM: a6293c739492b4ee2d4697abbb601e7c92729aea
diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved
index f406af44f5..511ea29a7b 100644
--- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-analytics-events",
"state" : {
- "revision" : "2f5fa5f1e2f6c6ae1a47c33d953a3ce289167eb0",
- "version" : "0.5.0"
+ "revision" : "44d5a0e898a71f8abbbe12afe9d73e82d370a9a1",
+ "version" : "0.15.0"
}
},
{
@@ -50,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift",
"state" : {
- "revision" : "0aa1308c43451fd077e332f72d6a32135f258834",
- "version" : "2.19.0"
+ "revision" : "f788fe2482c0b89019f679a1f43dccf9c25a0782",
+ "version" : "2.29.0"
}
},
{
@@ -84,7 +84,7 @@
{
"identity" : "swift-ogg",
"kind" : "remoteSourceControl",
- "location" : "https://github.com/vector-im/swift-ogg",
+ "location" : "https://github.com/element-hq/swift-ogg",
"state" : {
"branch" : "0.0.1",
"revision" : "e9a9e7601da662fd8b97d93781ff5c60b4becf88"
diff --git a/Riot/Assets/be.lproj/Vector.strings b/Riot/Assets/be.lproj/Vector.strings
index 8b13789179..64295bad49 100644
--- a/Riot/Assets/be.lproj/Vector.strings
+++ b/Riot/Assets/be.lproj/Vector.strings
@@ -1 +1,9 @@
+
+
+// Titles
+"title_home" = "ĐĐ°Đ»ĐŸŃĐœĐ°Ń";
+"people_empty_view_title" = "УЎзДлŃĐœŃĐșŃ";
+"group_details_home" = "ĐĐ°Đ»ĐŸŃĐœĐ°Ń";
+"spaces_home_space_title" = "ĐĐ°Đ»ĐŸŃĐœĐ°Ń";
+"title_people" = "УЎзДлŃĐœŃĐșŃ";
diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings
index 80eb951724..331614eca2 100644
--- a/Riot/Assets/de.lproj/Vector.strings
+++ b/Riot/Assets/de.lproj/Vector.strings
@@ -2616,8 +2616,8 @@
"manage_session_name_info" = "Sei dir bitte bewusst, dass Sitzungsnamen auch fĂŒr Personen, mit denen du kommunizierst, sichtbar sind. %@";
"manage_session_name_hint" = "Individuelle Sitzungsnamen können dir helfen, deine GerÀte einfacher zu erkennen.";
"user_other_session_filter" = "Filtern";
-"wysiwyg_composer_format_action_strikethrough" = "Unterstrichen formatieren";
-"wysiwyg_composer_format_action_underline" = "Durchgestrichen formatieren";
+"wysiwyg_composer_format_action_strikethrough" = "Durchgestrichen formatieren";
+"wysiwyg_composer_format_action_underline" = "Unterstrichen formatieren";
"wysiwyg_composer_format_action_italic" = "Kursiv formatieren";
// Formatting Actions
diff --git a/Riot/Assets/en.lproj/InfoPlist.strings b/Riot/Assets/en.lproj/InfoPlist.strings
index edab7c3751..c26e49651f 100644
--- a/Riot/Assets/en.lproj/InfoPlist.strings
+++ b/Riot/Assets/en.lproj/InfoPlist.strings
@@ -21,5 +21,5 @@
"NSContactsUsageDescription" = "In order to show who among your contacts already uses Tchap, we can exploit the e-mail addresses of your address book. These data will not be stored. For more information, please visit the privacy policy page in the app settings.";
"NSCalendarsUsageDescription" = "See your scheduled meetings in the app.";
"NSFaceIDUsageDescription" = "Face ID is used to access your app.";
-"NSLocationWhenInUseUsageDescription" = "When you share your location to people, Element needs access to show them a map.";
-"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you share your location to people, Element needs access to show them a map.";
+"NSLocationWhenInUseUsageDescription" = "When you share your location to people, Tchap needs access to show them a map.";
+"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you share your location to people, Tchap needs access to show them a map.";
diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings
index 0cb9b07a5b..06cc580561 100644
--- a/Riot/Assets/en.lproj/Vector.strings
+++ b/Riot/Assets/en.lproj/Vector.strings
@@ -378,7 +378,7 @@
"room_recents_conversations_section" = "ROOMS";
"room_recents_no_conversation" = "No rooms";
"room_recents_low_priority_section" = "LOW PRIORITY";
-"room_recents_server_notice_section" = "SYSTEM ALERTS";
+"room_recents_server_notice_section" = "Tchap Announcements"; // Tchap
"room_recents_invites_section" = "INVITES";
"room_recents_suggested_rooms_section" = "SUGGESTED ROOMS";
"room_recents_start_chat_with" = "Start chat";
@@ -727,7 +727,7 @@ Tap the + to start adding people.";
"settings_devices" = "SESSIONS";
"settings_cryptography" = "CRYPTOGRAPHY";
"settings_key_backup" = "KEY BACKUP";
-"settings_deactivate_account" = "DEACTIVATE ACCOUNT";
+"settings_deactivate_account" = "CLOSE ACCOUNT"; // Tchap
"settings_sign_out" = "Sign Out";
"settings_sign_out_confirmation" = "Are you sure?";
@@ -759,7 +759,7 @@ Tap the + to start adding people.";
"settings_security" = "SECURITY";
-"settings_enable_push_notif" = "Notifications on this device";
+"settings_enable_push_notif" = "Enable notifications on this device"; // Tchap
"settings_device_notifications" = "Device notifications";
"settings_show_decrypted_content" = "Show decrypted content";
"settings_global_settings_info" = "Global notification settings are available on your %@ web client";
@@ -860,7 +860,7 @@ Tap the + to start adding people.";
"settings_crypto_export" = "Export keys";
"settings_crypto_blacklist_unverified_devices" = "Encrypt to verified sessions only";
-"settings_deactivate_my_account" = "Deactivate account permanently";
+"settings_deactivate_my_account" = "Close account"; // Tchap
"settings_key_backup_info" = "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.";
"settings_key_backup_info_checking" = "CheckingâŠ";
@@ -1384,21 +1384,21 @@ Tap the + to start adding people.";
// Deactivate account
-"deactivate_account_title" = "Deactivate Account";
+"deactivate_account_title" = "Close Account"; // Tchap
"deactivate_account_informations_part1" = "This will make your account permanently unusable. You will not be able to log in, and no one will be able to re-register the same user ID. This will cause your account to leave all rooms it is participating in, and it will remove your account details from your identity server. ";
"deactivate_account_informations_part2_emphasize" = "This action is irreversible.";
-"deactivate_account_informations_part3" = "\n\nDeactivating your account ";
+"deactivate_account_informations_part3" = "\n\nClosing your account "; // Tchap
"deactivate_account_informations_part4_emphasize" = "does not by default cause us to forget messages you have sent. ";
"deactivate_account_informations_part5" = "If you would like us to forget your messages, please tick the box below\n\nMessage visibility in Matrix is similar to email. Our forgetting your messages means that messages you have sent will not be shared with any new or unregistered users, but registered users who already have access to these messages will still have access to their copy.";
-"deactivate_account_forget_messages_information_part1" = "Please forget all messages I have sent when my account is deactivated (";
+"deactivate_account_forget_messages_information_part1" = "Please forget all messages I have sent when my account is closed ("; // Tchap
"deactivate_account_forget_messages_information_part2_emphasize" = "Warning";
"deactivate_account_forget_messages_information_part3" = ": this will cause future users to see an incomplete view of conversations)";
-"deactivate_account_validate_action" = "Deactivate account";
+"deactivate_account_validate_action" = "Close account"; // Tchap
-"deactivate_account_password_alert_title" = "Deactivate Account";
+"deactivate_account_password_alert_title" = "Close Account"; // Tchap
"deactivate_account_password_alert_message" = "To continue, please enter your Matrix account password";
// Re-request confirmation dialog
@@ -2453,8 +2453,8 @@ Tap the + to start adding people.";
"location_sharing_settings_toggle_title" = "Enable location sharing";
"location_sharing_allow_background_location_title" = "Allow access";
-"location_sharing_allow_background_location_message" = "If youâd like to share your Live location, Element needs location access when the app is in the background.
-To enable access, tap Settings> Location and select Always";
+"location_sharing_allow_background_location_message" = "If youâd like to share your Live location, Tchap needs location access when the app is in the background.
+To enable access, tap Settings> Location and select Always"; // Tchap
"location_sharing_allow_background_location_validate_action" = "Settings";
"location_sharing_allow_background_location_cancel_action" = "Not now";
"location_sharing_map_credits_title" = "© Copyright";
@@ -2619,8 +2619,8 @@ To enable access, tap Settings> Location and select Always";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "Apply bold format";
"wysiwyg_composer_format_action_italic" = "Apply italic format";
-"wysiwyg_composer_format_action_underline" = "Apply strikethrough format";
-"wysiwyg_composer_format_action_strikethrough" = "Apply underline format";
+"wysiwyg_composer_format_action_underline" = "Apply underline format";
+"wysiwyg_composer_format_action_strikethrough" = "Apply strikethrough format";
"wysiwyg_composer_format_action_link" = "Apply link format";
"wysiwyg_composer_format_action_inline_code" = "Apply inline code format";
"wysiwyg_composer_format_action_unordered_list" = "Toggle bulleted list";
@@ -2773,7 +2773,7 @@ To enable access, tap Settings> Location and select Always";
"notice_room_history_visible_to_members_from_joined_point" = "%@ made future room history visible to all room members, from the point they joined.";
"notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ made future messages visible to everyone, from when they joined.";
"notice_crypto_unable_to_decrypt" = "** Unable to decrypt: %@ **";
-"notice_crypto_error_unknown_inbound_session_id" = "The sender's session has not sent us the keys for this message.";
+"notice_crypto_error_unknown_inbound_session_id" = "DecryptingâŠ"; // Tchap
"notice_sticker" = "sticker";
"notice_in_reply_to" = "In reply to";
"notice_voice_broadcast_live" = "Live broadcast";
diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings
index 5080a7ea52..22e9f34192 100644
--- a/Riot/Assets/et.lproj/Vector.strings
+++ b/Riot/Assets/et.lproj/Vector.strings
@@ -1889,8 +1889,8 @@
"notice_conference_call_started" = "VoIP rĂŒhmakĂ”ne algas";
"notice_conference_call_finished" = "VoIP rĂŒhmakĂ”ne lĂ”ppes";
// Notice Events with "You"
-"notice_room_invite_by_you" = "Sina kutsusid kasutajat %@";
-"notice_room_invite_you" = "%@ kutsus sind";
+"notice_room_invite_by_you" = "Sina saatsid kutse kasutajale %@";
+"notice_room_invite_you" = "%@ saatis sulle kutse";
"notice_room_third_party_invite_by_you" = "Sina saatsid kasutajale %@ kutse jututoaga liitumiseks";
"notice_room_third_party_registered_invite_by_you" = "Sina vÔtsid vastu kutse %@ nimel";
"notice_room_third_party_revoked_invite_by_you" = "Sina vÔtsid tagasi jututoaga liitumise kutse kasutajalt %@";
@@ -2025,7 +2025,7 @@
"notice_room_third_party_invite_for_dm" = "%@ saatis kutse kasutajale %@";
"notice_room_third_party_revoked_invite_for_dm" = "%@ vÔttis tagasi kasutaja %@ kutse";
"notice_room_name_changed_for_dm" = "%@ muutis jututoa uueks nimeks %@.";
-"notice_room_third_party_invite_by_you_for_dm" = "Sina kutsusid kasutajat %@";
+"notice_room_third_party_invite_by_you_for_dm" = "Sina saatsid kutse kasutajale %@";
"notice_room_third_party_revoked_invite_by_you_for_dm" = "Sina vÔtsid tagasi kasutaja %@ kutse";
"notice_room_name_changed_by_you_for_dm" = "Sa muutsid jututoa uueks nimeks %@.";
"notice_room_name_removed_by_you_for_dm" = "Sa eemaldasid jututoa nime";
@@ -2499,8 +2499,8 @@
"authentication_qr_login_start_title" = "Loe QR-koodi";
"authentication_login_with_qr" = "Logi sisse QR-koodi abil";
"wysiwyg_composer_format_action_strikethrough" = "Kasuta allajoonitud kirja";
-"wysiwyg_composer_format_action_underline" = "Kasuta lÀbijoonitud kirja";
-"wysiwyg_composer_format_action_italic" = "Kasuta kaldkirja";
+"wysiwyg_composer_format_action_italic" = "Kasuta lÀbijoonitud kirja";
+"wysiwyg_composer_format_action_underline" = "Kasuta kaldkirja";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "Kasuta paksu kirja";
diff --git a/Riot/Assets/fr.lproj/InfoPlist.strings b/Riot/Assets/fr.lproj/InfoPlist.strings
index 74885bea35..be710f7e8a 100644
--- a/Riot/Assets/fr.lproj/InfoPlist.strings
+++ b/Riot/Assets/fr.lproj/InfoPlist.strings
@@ -4,3 +4,5 @@
"NSMicrophoneUsageDescription" = "Tchap nécessite l'accÚs au microphone pour réaliser des vidéos avec la caméra, ou pour enregistrer des messages vocaux.";
"NSContactsUsageDescription" = "Afin dâafficher qui parmi vos contacts utilise dĂ©jĂ Tchap, nous pouvons exploiter les adresses e-mails de votre carnet d'adresse. Ces donnĂ©es ne seront pas mĂ©morisĂ©es. Pour plus d'informations, veuillez consulter les Termes et Conditions disponibles dans les paramĂštres de l'application.";
"NSFaceIDUsageDescription" = "Face ID est utilisé pour accéder à votre application.";
+"NSLocationWhenInUseUsageDescription" = "Lorsque vous partagez votre position, Tchap a besoin de votre autorisation pour l'afficher sur une carte.";
+"NSLocationAlwaysAndWhenInUseUsageDescription" = "Lorsque vous partagez votre position, Tchap a besoin de votre autorisation pour l'afficher sur une carte.";
diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings
index e48f772de3..4f5392ff72 100644
--- a/Riot/Assets/fr.lproj/Vector.strings
+++ b/Riot/Assets/fr.lproj/Vector.strings
@@ -279,7 +279,7 @@
"settings_add_phone_number" = "Ajouter un numéro de téléphone";
"settings_night_mode" = "Mode nuit";
"settings_fail_to_update_profile" = "Ăchec de la mise Ă jour du profil";
-"settings_enable_push_notif" = "Notifications sur cet appareil";
+"settings_enable_push_notif" = "Autoriser les notifications sur cet appareil"; // Tchap
"settings_global_settings_info" = "Les paramĂštres de notification globaux sont disponibles sur votre client web %@";
"settings_pin_rooms_with_missed_notif" = "Ăpingler les salons avec des notifications non lues";
"settings_pin_rooms_with_unread" = "Ăpingler les salons avec des messages non lus";
@@ -520,21 +520,21 @@
"room_action_send_photo_or_video" = "Envoyer une photo ou une vidéo";
"room_action_send_sticker" = "Envoyer un autocollant";
"room_action_send_file" = "Envoyer un fichier";
-"settings_deactivate_account" = "DĂSACTIVER LE COMPTE";
-"settings_deactivate_my_account" = "DĂ©sactiver le compte pour toujours";
+"settings_deactivate_account" = "FERMER LE COMPTE"; // Tchap
+"settings_deactivate_my_account" = "Fermer le compte"; // Tchap
"widget_sticker_picker_no_stickerpacks_alert" = "Vous nâavez aucun jeu dâautocollants activĂ©.";
"widget_sticker_picker_no_stickerpacks_alert_add_now" = "En ajouter maintenant ?";
-"deactivate_account_title" = "DĂ©sactiver le compte";
+"deactivate_account_title" = "Fermer le compte"; // Tchap
"deactivate_account_informations_part1" = "Votre compte sera inutilisable de façon permanente. Vous ne pourrez plus vous connecter et personne ne pourra se rĂ©enregistrer avec le mĂȘme identifiant dâutilisateur. Votre compte quittera tous les salons auxquels il participe, et toutes les informations du compte seront supprimĂ©s du serveur dâidentitĂ©. ";
"deactivate_account_informations_part2_emphasize" = "Cette action est irréversible.";
-"deactivate_account_informations_part3" = "\n\nLa désactivation de votre compte ";
+"deactivate_account_informations_part3" = "\n\nLa fermeture de votre compte "; // Tchap
"deactivate_account_informations_part4_emphasize" = "ne nous fait pas oublier les messages que vous avez envoyés par défaut. ";
"deactivate_account_informations_part5" = "Si vous souhaitez que nous oubliions vos messages, cochez la case ci-dessous\n\nLa visibilité des messages dans Matrix est similaire à celle des e-mails. Notre oubli des messages signifie que les messages que vous avez envoyés ne seront pas partagés avec les nouveaux utilisateurs ou les utilisateurs non enregistrés, mais les utilisateurs qui ont déjà accÚs à ces messages auront toujours accÚs à leur copie.";
-"deactivate_account_forget_messages_information_part1" = "Veuillez oublier tous les messages que jâai envoyĂ©s quand mon compte sera dĂ©sactivĂ© (";
+"deactivate_account_forget_messages_information_part1" = "Veuillez oublier tous les messages que jâai envoyĂ©s quand mon compte sera fermĂ© ("; // Tchap
"deactivate_account_forget_messages_information_part2_emphasize" = "Avertissement";
"deactivate_account_forget_messages_information_part3" = ": les futurs utilisateurs auront alors une vue incomplĂšte des conversations)";
-"deactivate_account_validate_action" = "DĂ©sactiver le compte";
-"deactivate_account_password_alert_title" = "DĂ©sactiver le compte";
+"deactivate_account_validate_action" = "Fermer le compte"; // Tchap
+"deactivate_account_password_alert_title" = "Fermer le compte"; // Tchap
"deactivate_account_password_alert_message" = "Pour continuer, veuillez renseigner votre mot de passe";
"event_formatter_rerequest_keys_part1" = "Demande envoyée.\U00A0";
"event_formatter_rerequest_keys_part2_link" = "Renvoyer";
@@ -551,7 +551,7 @@
"settings_labs_room_members_lazy_loading" = "Chargement différé des participants des salons";
"settings_labs_room_members_lazy_loading_error_message" = "Votre serveur d'accueil ne prend pas en charge le chargement différé des participants des salons. Réessayez plus tard.";
"room_event_action_view_decrypted_source" = "Voir la source déchiffrée";
-"room_recents_server_notice_section" = "ALERTES SYSTĂME";
+"room_recents_server_notice_section" = "Tchap Annonces"; // Tchap
"room_resource_limit_exceeded_message_contact_1" = " Veuillez ";
"room_resource_limit_exceeded_message_contact_2_link" = "contacter lâadministrateur de votre service";
"room_resource_limit_exceeded_message_contact_3" = " pour continuer Ă lâutiliser.";
@@ -1807,7 +1807,7 @@
"notice_room_history_visible_to_members_from_invited_point" = "%@ a rendu lâhistorique futur du salon visible Ă tous les membres, Ă partir du moment oĂč ils ont Ă©tĂ© invitĂ©s.";
"notice_room_history_visible_to_members_from_joined_point" = "%@ a rendu lâhistorique futur du salon visible Ă tous les membres, Ă partir de leur arrivĂ©e.";
"notice_crypto_unable_to_decrypt" = "** Déchiffrement impossible : %@ **";
-"notice_crypto_error_unknown_inbound_session_id" = "La session de lâexpĂ©diteur ne nous a pas envoyĂ© les clĂ©s pour ce message.";
+"notice_crypto_error_unknown_inbound_session_id" = "DĂ©chiffrement en coursâŠ"; // Tchap
"notice_sticker" = "autocollant";
// room display name
"room_displayname_empty_room" = "Salon vide";
@@ -2324,7 +2324,7 @@
"authentication_registration_title" = "Créez votre compte";
"room_access_settings_screen_upgrade_alert_title" = "Mettre Ă niveau le salon";
"room_access_settings_screen_upgrade_required" = "Mise Ă niveau requise";
-"location_sharing_allow_background_location_message" = "Si vous voulez partager votre localisation en temps rĂ©el, Element doit avoir accĂšs Ă vos donnĂ©es de localisation lorsque lâapplication est en arriĂšre-plan. Pour lui donner accĂšs, rendez-vous dans RĂ©glages > Position et sĂ©lectionnez Toujours";
+"location_sharing_allow_background_location_message" = "Si vous voulez partager votre localisation en temps rĂ©el, Tchap doit avoir accĂšs Ă vos donnĂ©es de localisation lorsque lâapplication est en arriĂšre-plan. Pour lui donner accĂšs, rendez-vous dans RĂ©glages > Position et sĂ©lectionnez Toujours"; // Tchap
"settings_timeline" = "HISTORIQUE";
"authentication_server_selection_login_title" = "Connexion Ă un serveur dâaccueil";
"message_reply_to_sender_sent_their_live_location" = "Localisation en temps réel.";
@@ -2669,8 +2669,8 @@
"wysiwyg_composer_format_action_unordered_list" = "Liste Ă puces";
"wysiwyg_composer_format_action_inline_code" = "Formater comme code informatique";
"wysiwyg_composer_format_action_link" = "Formater comme lien";
-"wysiwyg_composer_format_action_strikethrough" = "Souligner";
-"wysiwyg_composer_format_action_underline" = "Barrer";
+"wysiwyg_composer_format_action_strikethrough" = "Barrer";
+"wysiwyg_composer_format_action_underline" = "Souligner";
"wysiwyg_composer_format_action_italic" = "Mettre en italique";
// Formatting Actions
@@ -2797,4 +2797,3 @@
"local_contacts_access_discovery_warning" = "Afin dâafficher qui parmi vos contacts utilise dĂ©jĂ Tchap, nous pouvons exploiter les adresses e-mails de votre carnet d'adresse. Ces donnĂ©es ne seront pas mĂ©morisĂ©es. Pour plus d'informations, veuillez consulter les Termes et Conditions disponibles dans les paramĂštres de l'application.";
// Events formatter
"notice_crypto_unable_to_decrypt" = "Message verrouillé."; // Tchap
-"notice_crypto_error_unknown_inbound_session_id" = "Ouvrez Tchap sur un autre appareil pour récupérer vos messages."; // Tchap
diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings
index a77322f083..274042639a 100644
--- a/Riot/Assets/hu.lproj/Vector.strings
+++ b/Riot/Assets/hu.lproj/Vector.strings
@@ -2501,8 +2501,8 @@
"room_first_message_placeholder" = "KĂŒld el az elsĆ ĂŒzenetedetâŠ";
"authentication_qr_login_confirm_title" = "BiztonsĂĄgos kapcsolat beĂĄllĂtva";
"room_event_encryption_info_key_authenticity_not_guaranteed" = "A titkosĂtott ĂŒzenetek valĂłdisĂĄgĂĄt ezen az eszközön nem lehet garantĂĄlni.";
-"wysiwyg_composer_format_action_strikethrough" = "AlĂĄhĂșzott";
-"wysiwyg_composer_format_action_underline" = "ĂthĂșzott";
+"wysiwyg_composer_format_action_underline" = "AlĂĄhĂșzott";
+"wysiwyg_composer_format_action_strikethrough" = "ĂthĂșzott";
"wysiwyg_composer_format_action_italic" = "DĆlt";
// Formatting Actions
diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings
index 0a43411c32..5e8e1c027e 100644
--- a/Riot/Assets/id.lproj/Vector.strings
+++ b/Riot/Assets/id.lproj/Vector.strings
@@ -2782,8 +2782,8 @@
"authentication_qr_login_start_subtitle" = "Gunakan kamera pada perangkat ini untuk memindai kode QR yang ditampilkan di perangkat Anda yang lain:";
"authentication_qr_login_start_title" = "Pindai kode QR";
"authentication_login_with_qr" = "Masuk dengan kode QR";
-"wysiwyg_composer_format_action_strikethrough" = "Terapkan format garis bawah";
-"wysiwyg_composer_format_action_underline" = "Terapkan format coret";
+"wysiwyg_composer_format_action_underline" = "Terapkan format garis bawah";
+"wysiwyg_composer_format_action_strikethrough" = "Terapkan format coret";
"wysiwyg_composer_format_action_italic" = "Terapkan format miring";
// Formatting Actions
diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings
index 5658e74789..0e893b97cb 100644
--- a/Riot/Assets/it.lproj/Vector.strings
+++ b/Riot/Assets/it.lproj/Vector.strings
@@ -2555,8 +2555,8 @@
"authentication_qr_login_start_subtitle" = "Usa la fotocamera di questo dispositivo per scansionare il codice QR mostrato nell'altro dispositivo:";
"authentication_qr_login_start_title" = "Scansiona codice QR";
"authentication_login_with_qr" = "Accedi con codice QR";
-"wysiwyg_composer_format_action_strikethrough" = "Applica formato sottolineato";
-"wysiwyg_composer_format_action_underline" = "Applica formato sbarrato";
+"wysiwyg_composer_format_action_underline" = "Applica formato sottolineato";
+"wysiwyg_composer_format_action_strikethrough" = "Applica formato sbarrato";
"wysiwyg_composer_format_action_italic" = "Applica formato corsivo";
// Formatting Actions
diff --git a/Riot/Assets/ja.lproj/Vector.strings b/Riot/Assets/ja.lproj/Vector.strings
index c1d7678303..0dfb2562ba 100644
--- a/Riot/Assets/ja.lproj/Vector.strings
+++ b/Riot/Assets/ja.lproj/Vector.strings
@@ -2752,8 +2752,8 @@
"notice_error_unformattable_event" = "** ăĄăă»ăŒăžăæç»ă§ăăŸăăăäžć
·ćăć ±ćăăŠăă ăă";
"wysiwyg_composer_format_action_un_indent" = "ă€ăłăăłăăæžăă";
"wysiwyg_composer_format_action_indent" = "ă€ăłăăłăăćąăă";
-"wysiwyg_composer_format_action_strikethrough" = "äžç·ă§èŁ
éŁŸ";
-"wysiwyg_composer_format_action_underline" = "æăĄæ¶ăç·ă§èŁ
éŁŸ";
+"wysiwyg_composer_format_action_underline" = "äžç·ă§èŁ
éŁŸ";
+"wysiwyg_composer_format_action_strikethrough" = "æăĄæ¶ăç·ă§èŁ
éŁŸ";
// MARK: - WYSIWYG Composer
diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings
index a814c281b5..f0ddd9d106 100644
--- a/Riot/Assets/nl.lproj/Vector.strings
+++ b/Riot/Assets/nl.lproj/Vector.strings
@@ -2646,8 +2646,8 @@
"invite_to" = "Uitnodigen %@";
"room_event_encryption_info_key_authenticity_not_guaranteed" = "De authenticiteit van dit versleutelde bericht kan niet worden gegarandeerd op dit apparaat.";
"deselect_all" = "Deselecteer alles";
-"wysiwyg_composer_format_action_strikethrough" = "Onderstrepen formaat toepassen";
-"wysiwyg_composer_format_action_underline" = "Doorstrepen formaat toepassen";
+"wysiwyg_composer_format_action_underline" = "Onderstrepen formaat toepassen";
+"wysiwyg_composer_format_action_strikethrough" = "Doorstrepen formaat toepassen";
"wysiwyg_composer_format_action_italic" = "Cursief formaat toepassen";
// Formatting Actions
diff --git a/Riot/Assets/peo.lproj/InfoPlist.strings b/Riot/Assets/peo.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/Riot/Assets/peo.lproj/InfoPlist.strings
@@ -0,0 +1 @@
+
diff --git a/Riot/Assets/pl.lproj/Vector.strings b/Riot/Assets/pl.lproj/Vector.strings
index 21be89f5de..ba5a2bc091 100644
--- a/Riot/Assets/pl.lproj/Vector.strings
+++ b/Riot/Assets/pl.lproj/Vector.strings
@@ -2694,8 +2694,8 @@
"wysiwyg_composer_format_action_unordered_list" = "PrzeĆÄ
cz listÄ punktorĂłw";
"wysiwyg_composer_format_action_inline_code" = "Zastosuj kod w tekĆcie";
"wysiwyg_composer_format_action_link" = "Zastosuj link";
-"wysiwyg_composer_format_action_strikethrough" = "Zastosuj podkreĆlenie";
-"wysiwyg_composer_format_action_underline" = "Zastosuj przekreĆlenie";
+"wysiwyg_composer_format_action_underline" = "Zastosuj podkreĆlenie";
+"wysiwyg_composer_format_action_strikethrough" = "Zastosuj przekreĆlenie";
"wysiwyg_composer_format_action_italic" = "Zastosuj kursywÄ";
// Formatting Actions
diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings
index d60ed99b62..991716cd9f 100644
--- a/Riot/Assets/pt_BR.lproj/Vector.strings
+++ b/Riot/Assets/pt_BR.lproj/Vector.strings
@@ -2556,8 +2556,8 @@
"authentication_qr_login_start_subtitle" = "Use a cĂąmera neste dispositivo para scannar o QR code mostrado em seu outro dispositivo:";
"authentication_qr_login_start_title" = "Scannar QR code";
"authentication_login_with_qr" = "Fazer signin com QR code";
-"wysiwyg_composer_format_action_strikethrough" = "Aplicar formato sublinhar";
-"wysiwyg_composer_format_action_underline" = "Aplicar formato tachar";
+"wysiwyg_composer_format_action_underline" = "Aplicar formato sublinhar";
+"wysiwyg_composer_format_action_strikethrough" = "Aplicar formato tachar";
"wysiwyg_composer_format_action_italic" = "Aplicar formato itĂĄlico";
// Formatting Actions
diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings
index 6f4eda6a4a..a18873e4d8 100644
--- a/Riot/Assets/sk.lproj/Vector.strings
+++ b/Riot/Assets/sk.lproj/Vector.strings
@@ -2778,8 +2778,8 @@
"authentication_qr_login_start_subtitle" = "Pomocou fotoaparĂĄtu na tomto zariadenĂ naskenujte QR kĂłd zobrazenĂœ na vaĆĄom druhom zariadenĂ:";
"authentication_qr_login_start_title" = "SkenovaƄ QR kód";
"authentication_login_with_qr" = "PrihlåsiƄ sa pomocou QR kódu";
-"wysiwyg_composer_format_action_strikethrough" = "PouĆŸiĆ„ formĂĄt podÄiarknutia";
-"wysiwyg_composer_format_action_underline" = "PouĆŸiĆ„ formĂĄt preÄiarknutia";
+"wysiwyg_composer_format_action_underline" = "PouĆŸiĆ„ formĂĄt podÄiarknutia";
+"wysiwyg_composer_format_action_strikethrough" = "PouĆŸiĆ„ formĂĄt preÄiarknutia";
"wysiwyg_composer_format_action_italic" = "PouĆŸiĆ„ formĂĄt kurzĂvou";
// Formatting Actions
diff --git a/Riot/Assets/sq.lproj/InfoPlist.strings b/Riot/Assets/sq.lproj/InfoPlist.strings
index a4d354d560..e2f9af626f 100644
--- a/Riot/Assets/sq.lproj/InfoPlist.strings
+++ b/Riot/Assets/sq.lproj/InfoPlist.strings
@@ -5,5 +5,5 @@
"NSContactsUsageDescription" = "Do tâi jepen shĂ«rbyesit tuaj tĂ« identiteteve, pĂ«r ta ndihmuar tĂ« gjejĂ« kontakte tuajt nĂ« Matrix.";
"NSCalendarsUsageDescription" = "Shihini te aplikacioni takimet tuaja të planifikuara.";
"NSFaceIDUsageDescription" = "Face ID përdoret që të hyni në aplikacionin tuaj.";
-"NSLocationWhenInUseUsageDescription" = "Kur ndani vendndodhjen tuaj me persona, Element-i ka nevojĂ« pĂ«r hyrje nĂ« tĂ«, qĂ« tâu trgojĂ« atyre njĂ« hartĂ«.";
+"NSLocationWhenInUseUsageDescription" = "Kur u tregoni vendndodhjen tuaj tĂ« tjerĂ«ve, Element-i ka nevojĂ« pĂ«r hyrje nĂ« tĂ«, qĂ« tâu tregojĂ« atyre njĂ« hartĂ«.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Kur u tregoni vendndodhjen tuaj tĂ« tjerĂ«ve, Element-it i duhet hyrje pĂ«r tâu shfaqur njĂ« hartĂ«.";
diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings
index 1dc4968b50..cd338d626b 100644
--- a/Riot/Assets/sq.lproj/Vector.strings
+++ b/Riot/Assets/sq.lproj/Vector.strings
@@ -127,8 +127,8 @@
"contacts_user_directory_offline_section" = "DREJTORI PĂRDORUESISH (jashtĂ« linje)";
// Chat participants
"room_participants_title" = "Pjesëmarrës";
-"room_participants_add_participant" = "Shtoni pjesmarrës";
-"room_participants_one_participant" = "1 pjesmarrës";
+"room_participants_add_participant" = "Shtoni pjesëmarrës";
+"room_participants_one_participant" = "1 pjesëmarrës";
"room_participants_multi_participants" = "%d pjesëmarrës";
"room_participants_leave_prompt_title" = "Dilni nga dhomë";
"room_participants_leave_prompt_msg" = "Jeni i sigurt se doni të ikni nga dhoma?";
@@ -317,7 +317,7 @@
"group_home_one_room_format" = "1 dhomë";
"group_invitation_format" = "%@ ju ftoi të bëheni pjesë e kësaj bashkësie";
// Group participants
-"group_participants_add_participant" = "Shtoni pjesmarrës";
+"group_participants_add_participant" = "Shtoni pjesëmarrës";
"group_participants_leave_prompt_title" = "Braktiseni grupin";
"group_participants_leave_prompt_msg" = "Jeni i sigurt se doni ta braktisni grupin?";
"group_participants_remove_prompt_title" = "Ripohim";
@@ -398,7 +398,7 @@
"auth_add_email_and_phone_warning" = "Regjistrimi me email dhe me numër telefoni njëherazi nuk mbulohet ende, deri sa të ketë API. Do të merret parasysh vetëm numri i telefonit. Email-in tuaj mund ta shtoni te profili juaj, te rregullimet.";
"room_creation_appearance_picture" = "Foto fjalosjeje (në daçi)";
"room_creation_invite_another_user" = "ID Përdoruesi, emër ose email";
-"room_recents_favourites_section" = "TĂ PARAPALQYERA";
+"room_recents_favourites_section" = "TĂ PARAPĂLQYERA";
"room_recents_server_notice_section" = "SINJALIZIME SISTEMI";
"room_recents_join_room_title" = "Hyni në një dhomë";
"room_participants_invite_another_user" = "KĂ«rkoni / ftoni sipas ID-je PĂ«rdoruesi, Emri ose email-i";
@@ -507,7 +507,7 @@
"room_details_history_section_members_only" = "Vetëm anëtarët (që nga çasti i përzgjedhjes së kësaj mundësie)";
"room_details_history_section_prompt_msg" = "Ndryshime se cilët mund të lexojnë historikun do të vlejnë vetëm për mesazhe të ardhshëm në këtë dhomë. Dukshmëria e historikut ekzistues nuk do të ndryshohet.";
"room_details_addresses_disable_main_address_prompt_msg" = "Sâdo tĂ« keni adresĂ« kryesore tĂ« specifikuar. Adresa kryesore parazgjedhje pĂ«r kĂ«tĂ« dhomĂ« do tĂ« zgjidhet nĂ« tym";
-"room_details_advanced_enable_e2e_encryption" = "Aktivizo fshehtĂ«zim (kujdes: sâmund tĂ« çaktizohet mĂ«!)";
+"room_details_advanced_enable_e2e_encryption" = "Aktivizo fshehtĂ«zim (kujdes: sâmund tĂ« çaktivizohet mĂ«!)";
"room_details_advanced_e2e_encryption_prompt_message" = "FshehtĂ«zimi skaj-mĂ«-skaj Ă«shtĂ« eksperimental dhe mund tĂ« mos jetĂ« i qĂ«ndrueshĂ«m.\n\nSâduhet tâi zini ende besĂ« pĂ«r sigurim tĂ« dhĂ«nash.\n\nPajisjet sâdo tĂ« jenĂ« ende nĂ« gjendje tĂ« shfshehtĂ«zojnĂ« historik nga periudha pĂ«rpara se tĂ« merrnin pjesĂ« te dhomĂ«.\n\nPasi tĂ« jetĂ« aktivizuar fshehtĂ«zimi pĂ«r njĂ« dhomĂ«, sâmund tĂ« çaktivizohet mĂ« (hĂ«pĂ«rhĂ«).\n\nMesazhet e fshehtĂ«zuar sâdo tĂ« jenĂ« tĂ« dukshĂ«m nĂ« klientĂ« qĂ« nuk sendĂ«rtojnĂ« ende fshehtĂ«zimin.";
"room_details_fail_to_update_room_guest_access" = "Sâarrihet tĂ« pĂ«rditĂ«sohet mundĂ«sia e hyrjes nĂ« dhomĂ« tĂ« vizitorĂ«ve";
"group_participants_invite_malformed_id" = "ID e keqformuar. Duhet të jetë një ID Matrix, si '@localpart:domain'";
@@ -619,7 +619,7 @@
"key_backup_setup_intro_setup_action_without_existing_backup" = "Fillo të përdorësh Kopjeruajtje Kyçesh";
"key_backup_setup_intro_setup_action_with_existing_backup" = "Përdor Kopjeruajtje Kyçesh";
"key_backup_setup_passphrase_title" = "Sigurojeni kopjeruajtjen tuaj me një Frazë Sigurie";
-"key_backup_setup_passphrase_setup_recovery_key_info" = "Ose, sigurojeni kopjeruajtjen tuaj me një Kyç Sigurie, duke e ruajtur këtë diku të parrezikuar.";
+"key_backup_setup_passphrase_setup_recovery_key_info" = "Ose, sigurojeni kopjeruajtjen tuaj me një Kyç Sigurie, duke e ruajtur këtë diku të parrezik.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Të mëtejshme) Rregullojeni me një Kyç Sigurie";
"key_backup_setup_success_title" = "Sukses!";
// Success from passphrase
@@ -627,7 +627,7 @@
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Ruani Kyç Sigurie";
"key_backup_setup_success_from_passphrase_done_action" = "U krye";
// Success from recovery key
-"key_backup_setup_success_from_recovery_key_info" = "Po bëhet kopjeruajtja për kyçet tuaj.\n\nBëni një kopje të këtij Kyçi Sigurie dhe mbajeni të parrezikuar.";
+"key_backup_setup_success_from_recovery_key_info" = "Po bëhet kopjeruajtja për kyçet tuaj.\n\nBëni një kopje të këtij Kyçi Sigurie dhe mbajeni të parrezik.";
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Kyç Sigurie";
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Bëni një Kopje";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "Kam bërë një kopje";
@@ -1129,7 +1129,7 @@
"secure_key_backup_setup_existing_backup_error_info" = "Shkyçeni, që ta ripërdorni te kopjeruajtja e sigurt ose për ta fshirë që të krijoni një kopjeruajtje të re mesazhesh te kopjeruajtja e sigurt.";
"secure_key_backup_setup_existing_backup_error_unlock_it" = "Shkyçe";
"secure_key_backup_setup_existing_backup_error_delete_it" = "Fshije";
-"sign_out_non_existing_key_backup_alert_setup_secure_backup_action" = "Fillo të përdorësh Kojperuajtje të Sigurt";
+"sign_out_non_existing_key_backup_alert_setup_secure_backup_action" = "Fillo të përdorësh Kopjeruajtje të Sigurt";
"security_settings_crypto_sessions_description_2" = "Nëse nuk njihni një palë kredenciale, ndryshoni fjalëkalimin tuaj Matrix dhe riujdisni Kopjeruajtjen e Sigurt.";
"cross_signing_setup_banner_title" = "Ujdisni fshehtëzim";
"cross_signing_setup_banner_subtitle" = "Verifikoni më me lehtësi pajisje të tjera";
@@ -1517,7 +1517,7 @@
"poll_timeline_vote_not_registered_action" = "OK";
"poll_timeline_vote_not_registered_subtitle" = "Na ndjeni, vota juaj sâu regjistrua, ju lutemi, riprovoni";
"poll_timeline_vote_not_registered_title" = "Votë e paregjistruar";
-"poll_timeline_total_final_results" = "Rezultati përfundimtar, bazua në %lu votë";
+"poll_timeline_total_final_results" = "Rezultati përfundimtar, bazua në %lu vota";
"poll_timeline_total_final_results_one_vote" = "Rezultati përfundimtar, bazua në 1 votë";
"poll_timeline_total_votes_not_voted" = "%lu vota të hedhura. Që të shihni përfundimet, votoni";
"poll_timeline_total_one_vote_not_voted" = "1 votë e hedhur. Që të shihni përfundimet, votoni";
@@ -1975,9 +1975,9 @@
// Room members
"room_member_ignore_prompt" = "Doni të fshihen krejt mesazhet nga ky përdorues?";
"room_member_power_level_prompt" = "Sâdo tĂ« jeni nĂ« gjendje ta zhbĂ«ni kĂ«tĂ« ndryshim, ngaqĂ« po e promovoni pĂ«rdoruesin tĂ« ketĂ« tĂ« njĂ«jtĂ«n shkallĂ« pushteti si ju vetĂ«.\nJeni i sigurt?";
-"attachment_e2e_keys_file_prompt" = "Kjo kartelë përmban kyçe fshehtëzimi të eksportur nga një klient Matrix.\nDoni të shihni lëndën e kartelës apo të importoni kyçet që ajo përmban?";
+"attachment_e2e_keys_file_prompt" = "Kjo kartelë përmban kyçe fshehtëzimi të eksportuar nga një klient Matrix.\nDoni të shihni lëndën e kartelës apo të importoni kyçet që ajo përmban?";
"e2e_import_prompt" = "Ky proces ju lejon të importoni kyçe fshehtëzimi që keni eksportuar më parë nga një tjetër klient Matrix. Mandej do të jeni në gjendje të shfshehtëzoni çfarëdo mesazhesh që mund të shfshehtëzojë ai klient tjetër.\nKartela e eksportit është e mbrojtur me një frazëkalim. Që të shfshehtëzoni kartelën, duhet ta jepni frazëkalimin këtu.";
-"e2e_export_prompt" = "Ky proces ju lejon tĂ« eksportoni te njĂ« kartelĂ« vendore kyçet pĂ«r mesazhe qĂ« keni marrĂ« nĂ« dhoma tĂ« fshehtĂ«zuara. Mandej do tĂ« jeni nĂ« gjendje ta importoni kartelĂ«n te njĂ« tjetĂ«r klient Matrix nĂ« tĂ« ardhmen, qĂ« kĂ«shtu ai klient tĂ« jetĂ« nĂ« gjendje tâi fshehtĂ«zojĂ« kĂ«to mesazhe.\nKartela e eksportuar do tâi lejojĂ«, cilitdo qĂ« mund ta lexojĂ«, tĂ« shfshehtĂ«zojĂ« çfarĂ«do mesazhesh tĂ« fshehtĂ«zuar qĂ« mund tĂ« shihni ju, ndaj duhet tĂ« bĂ«ni kujdes ta mbani tĂ« parrezikuar.";
+"e2e_export_prompt" = "Ky proces ju lejon tĂ« eksportoni te njĂ« kartelĂ« vendore kyçet pĂ«r mesazhe qĂ« keni marrĂ« nĂ« dhoma tĂ« fshehtĂ«zuara. Mandej do tĂ« jeni nĂ« gjendje ta importoni kartelĂ«n te njĂ« tjetĂ«r klient Matrix nĂ« tĂ« ardhmen, qĂ« kĂ«shtu ai klient tĂ« jetĂ« nĂ« gjendje tâi fshehtĂ«zojĂ« kĂ«to mesazhe.\nKartela e eksportuar do tâi lejojĂ«, cilitdo qĂ« mund ta lexojĂ«, tĂ« shfshehtĂ«zojĂ« çfarĂ«do mesazhesh tĂ« fshehtĂ«zuar qĂ« mund tĂ« shihni ju, ndaj duhet tĂ« bĂ«ni kujdes ta mbani tĂ« parrezik.";
"error_common_message" = "Ndodhi një gabim. Ju lutemi, riprovoni më vonë.";
// Permissions
"camera_access_not_granted_for_call" = "Thirrjet video lypin pĂ«rdorim tĂ« KamerĂ«s, por %@ sâka leje pĂ«r ta pĂ«rdorur";
@@ -1988,7 +1988,7 @@
"redact" = "Hiqe";
// contacts list screen
"invitation_message" = "Do të doja të bisedoja me ju me Matrix. Për të pasur më tepër itë dhëna, ju lutem, vizitoni sajtin http://matrix.org.";
-"notification_settings_global_info" = "Rregullimet mbi njoftimet ruhen te llogaria juaj e përdoruesit dhe ndahen me krejt klientët që i mbulojnë ato (përfshi njoftimet në desktop).\n\nRregullat zbatohen sipas një radhe; rregulli i parë që ka përputhje përcakton lëndën për mesazhin.\nKështu: njoftimet sipas fjalësh janë më të rëndësishme se njoftimet sipas dhomash të cilat janë më të rëndësishme se njoftimet sipas dërguesish.\nFor multiple rules of the same kind, the first one in the list that matches takes priority.";
+"notification_settings_global_info" = "Rregullimet mbi njoftimet ruhen te llogaria juaj e përdoruesit dhe ndahen me krejt klientët që i mbulojnë ato (përfshi njoftimet në desktop).\n\nRregullat zbatohen sipas një radhe; rregulli i parë që ka përputhje përcakton lëndën për mesazhin.\nKështu: njoftimet sipas fjalësh janë më të rëndësishme se njoftimet sipas dhomash të cilat janë më të rëndësishme se njoftimet sipas dërguesish.\nPër rregulla të shumta të të njëjtit lloj, i pari në listë që ka përputhje ka përparësinë.";
"notification_settings_per_word_notifications" = "Njoftime sipas fjale";
"notification_settings_per_word_info" = "Për fjalët përputhjet gjenden pa marrë parasysh shkrimin me të madhe apo të vogël, dhe mund të përfshijnë një shenjë të gjithëpushtetshme *. Kështu:\nkot përputhet me vargun kot të rrethuar nga përkufizues fjalësh (p.sh. shenja pikësimi apo hapësira, ose fillim/fund rreshti).\nkot* përputhet me çfarëdo fjale që fillon me kot.\n*kot* përputhet me çfarëdo fjale që përfshin 3 shkronjat kot.";
"notification_settings_per_room_notifications" = "Njoftime sipas dhome";
@@ -2420,9 +2420,9 @@
"all_chats_edit_layout_add_section_message" = "Fiksoni ndarje te kreu, për hyrje të lehtë në ta";
"room_event_encryption_info_key_authenticity_not_guaranteed" = "Sâmund tĂ« garantohet mirĂ«filltĂ«sia e kĂ«tij mesazhi tĂ« fshehtĂ«zuar nĂ« kĂ«tĂ« pajisje.";
"deselect_all" = "Shpërzgjidhi Krejt";
-"wysiwyg_composer_format_action_strikethrough" = "Apliko format me të nënvizuara";
-"wysiwyg_composer_format_action_underline" = "Apliko format me të hequravije";
-"wysiwyg_composer_format_action_italic" = "Apliko format me të pjerrta";
+"wysiwyg_composer_format_action_underline" = "Apliko format me të nënvizuara";
+"wysiwyg_composer_format_action_strikethrough" = "Apliko format me të hequravije";
+"wysiwyg_composer_format_action_italic" = "Apliko format me të pjerrëta";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "Apliko format me të trasha";
@@ -2448,7 +2448,7 @@
"user_session_details_device_os" = "Sistem Operativ";
"user_session_details_device_browser" = "Shfletues";
"user_session_details_device_model" = "Model";
-"user_session_details_device_ip_location" = "Venndodhje IP-je";
+"user_session_details_device_ip_location" = "Vendndodhje IP-je";
"user_session_details_device_ip_address" = "Adresë IP";
"user_session_details_last_activity" = "Veprimtaria e fundit";
"user_session_details_session_section_footer" = "Kopjoni çfarëdo të dhëne duke prekur mbi të dhe duke e mbajtur të shtypur.";
@@ -2660,7 +2660,7 @@
"wysiwyg_composer_format_action_link" = "Apliko formatim lidhjeje";
"notice_voice_broadcast_ended_by_you" = "Përfunduar një transmetim zanor.";
"notice_voice_broadcast_ended" = "%@ përfundoi një transmetim zanor.";
-"notice_voice_broadcast_live" = "Transmetim i drejtëpërdrejtë";
+"notice_voice_broadcast_live" = "Transmetim i drejtpërdrejtë";
"user_other_session_security_recommendation_title" = "Sesione të tjerë";
"poll_timeline_ended_text" = "Përfundoi pyetësori";
"poll_timeline_decryption_error" = "Për shkak gabimesh shfshehtëzimi, mund të mos jenë numëruar disa vota";
diff --git a/Riot/Assets/sv.lproj/Vector.strings b/Riot/Assets/sv.lproj/Vector.strings
index cb9918a3a4..85662f0292 100644
--- a/Riot/Assets/sv.lproj/Vector.strings
+++ b/Riot/Assets/sv.lproj/Vector.strings
@@ -2485,8 +2485,8 @@
"wysiwyg_composer_format_action_unordered_list" = "VĂ€xla punktlista";
"wysiwyg_composer_format_action_inline_code" = "TillÀmpa inline-kodstil";
"wysiwyg_composer_format_action_link" = "TillÀmpa lÀnkformat";
-"wysiwyg_composer_format_action_strikethrough" = "TillÀmpa understruken stil";
-"wysiwyg_composer_format_action_underline" = "TillÀmpa genomstruken stil";
+"wysiwyg_composer_format_action_underline" = "TillÀmpa understruken stil";
+"wysiwyg_composer_format_action_strikethrough" = "TillÀmpa genomstruken stil";
"wysiwyg_composer_format_action_italic" = "TillÀmpa kursiv stil";
// Formatting Actions
diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings
index 04e9b75d54..7c18d9f8b7 100644
--- a/Riot/Assets/uk.lproj/Vector.strings
+++ b/Riot/Assets/uk.lproj/Vector.strings
@@ -2780,8 +2780,8 @@
"authentication_qr_login_start_subtitle" = "ĐĐžĐșĐŸŃĐžŃŃĐŸĐČŃĐčŃĐ” ĐșĐ°ĐŒĐ”ŃŃ ĐœĐ° ŃŃĐŸĐŒŃ ĐżŃĐžŃŃŃĐŸŃ, ŃĐŸĐ± Đ·ŃŃĐșĐ°ĐœŃĐČĐ°ŃĐž QR-ĐșĐŸĐŽ, ĐżĐŸĐșĐ°Đ·Đ°ĐœĐžĐč ĐœĐ° ŃĐœŃĐŸĐŒŃ ĐżŃĐžŃŃŃĐŸŃ:";
"authentication_qr_login_start_title" = "ĐĄĐșĐ°ĐœŃĐČĐ°ŃĐž QR-ĐșĐŸĐŽ";
"authentication_login_with_qr" = "ĐŁĐČŃĐčŃĐž ĐČĐžĐșĐŸŃĐžŃŃĐ°ĐČŃĐž QR-ĐșĐŸĐŽ";
-"wysiwyg_composer_format_action_strikethrough" = "ĐĐ°ŃŃĐŸŃŃĐČĐ°ŃĐž ŃĐŸŃĐŒĐ°ŃŃĐČĐ°ĐœĐœŃ ĐżŃĐŽĐșŃĐ”ŃĐ»Đ”ĐœĐžĐŒ";
-"wysiwyg_composer_format_action_underline" = "ĐĐ°ŃŃĐŸŃŃĐČĐ°ŃĐž ŃĐŸŃĐŒĐ°ŃŃĐČĐ°ĐœĐœŃ ĐżĐ”ŃĐ”ĐșŃĐ”ŃĐ»Đ”ĐœĐžĐŒ";
+"wysiwyg_composer_format_action_underline" = "ĐĐ°ŃŃĐŸŃŃĐČĐ°ŃĐž ŃĐŸŃĐŒĐ°ŃŃĐČĐ°ĐœĐœŃ ĐżŃĐŽĐșŃĐ”ŃĐ»Đ”ĐœĐžĐŒ";
+"wysiwyg_composer_format_action_strikethrough" = "ĐĐ°ŃŃĐŸŃŃĐČĐ°ŃĐž ŃĐŸŃĐŒĐ°ŃŃĐČĐ°ĐœĐœŃ ĐżĐ”ŃĐ”ĐșŃĐ”ŃĐ»Đ”ĐœĐžĐŒ";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "ĐĐ°ŃŃĐŸŃŃĐČĐ°ŃĐž ŃĐŸŃĐŒĐ°ŃŃĐČĐ°ĐœĐœŃ Đ¶ĐžŃĐœĐžĐŒ";
diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings
index 79177b974e..50d43aed9a 100644
--- a/Riot/Assets/zh_Hans.lproj/Vector.strings
+++ b/Riot/Assets/zh_Hans.lproj/Vector.strings
@@ -928,7 +928,7 @@
// MARK: Emoji picker
"emoji_picker_title" = "ććș";
"emoji_picker_people_category" = "èĄšæ
ćäșșç©";
-"emoji_picker_nature_category" = "ćšç©ćèȘç";
+"emoji_picker_nature_category" = "ćšç©ćèȘç¶";
"emoji_picker_foods_category" = "éŁç©ćé„źæ";
"emoji_picker_activity_category" = "掻ćš";
"emoji_picker_places_category" = "æ
æžžćæŻçč";
@@ -952,7 +952,7 @@
"key_verification_tile_request_incoming_approval_decline" = "æç»";
"key_verification_tile_conclusion_done_title" = "ć·ČéȘèŻ";
"key_verification_tile_conclusion_warning_title" = "äžèą«äżĄä»»çç»ćœ";
-"key_verification_incoming_request_incoming_alert_message" = "%@æłèŠéȘèŻ";
+"key_verification_incoming_request_incoming_alert_message" = "%@ æłèŠèżèĄéȘèŻ";
"user_verification_start_verify_action" = "ćŒć§éȘèŻ";
"user_verification_start_information_part1" = "äžșäșéąć€çćźć
šæ§ïŒèŻ·éȘèŻïŒ ";
"user_verification_start_information_part2" = " æŁæ„ćšäœ ç䞀äžȘèźŸć€äžçäžæŹĄæ§ä»Łç ă";
@@ -2405,3 +2405,24 @@
"room_access_space_chooser_known_spaces_section" = "æšç„éçć
ć« %@ çç©șéŽ";
"room_access_space_chooser_other_spaces_section_info" = "èżäșćŸćŻèœæŻ %@ ç知çććäžă";
"room_access_space_chooser_other_spaces_section" = "ć
¶ä»ç©șéŽææżéŽ";
+"event_formatter_message_deleted" = "æ¶æŻć·Čć é€";
+"network_offline_title" = "æšć·Č犻çșż";
+
+// MARK: Sign out warning
+
+"sign_out" = "ç»ćș";
+
+// Unverified sessions
+"key_verification_alert_title" = "æšææȘéȘèŻçäŒèŻ";
+"pill_message_in" = "ćš %@ éçæ¶æŻ";
+
+// Legacy to Rust security upgrade
+
+"key_verification_self_verify_security_upgrade_alert_title" = "ćșçšć·ČæŽæ°";
+"sign_out_confirmation_message" = "æšçĄźćźèŠç»ćșćïŒ";
+"device_verification_self_verify_open_on_other_device_title" = "ćšæšçćŠäžć°èźŸć€äžæćŒ %@";
+"device_verification_self_verify_open_on_other_device_information" = "æšéèŠć
éȘèŻæ€äŒèŻæèœèŻ»ćć ćŻäżĄæŻă\n\nćšæšçć
¶ä»èźŸć€äžæćŒ Element ćč¶æç
§èŻŽæèżèĄæäœă";
+"device_verification_self_verify_wait_recover_secrets_additional_help" = "ć·Čæ æłèźżéź %@ äŒèŻïŒ";
+"network_offline_message" = "æšć·Č犻çșżïŒèŻ·æŁæ„æšççœç»éŸæ„ă";
+"key_verification_alert_body" = "éæ°æŁæ„仄祟äżæšç莊æ·ćźć
šă";
+"key_verification_scan_qr_code_title" = "æ«æ QR Code";
diff --git a/Riot/Assets/zh_Hant.lproj/Vector.strings b/Riot/Assets/zh_Hant.lproj/Vector.strings
index f92adb766d..f7818a7907 100644
--- a/Riot/Assets/zh_Hant.lproj/Vector.strings
+++ b/Riot/Assets/zh_Hant.lproj/Vector.strings
@@ -2026,8 +2026,8 @@
// Links
"wysiwyg_composer_link_action_text" = "æć";
"wysiwyg_composer_format_action_link" = "ć„çšéŁç”æ ŒćŒ";
-"wysiwyg_composer_format_action_strikethrough" = "ć„çšćșç·æ ŒćŒ";
-"wysiwyg_composer_format_action_underline" = "ć„çšćȘé€ç·æ ŒćŒ";
+"wysiwyg_composer_format_action_underline" = "ć„çšćșç·æ ŒćŒ";
+"wysiwyg_composer_format_action_strikethrough" = "ć„çšćȘé€ç·æ ŒćŒ";
"wysiwyg_composer_format_action_italic" = "ć„çšçŸ©ćŒæé«æ ŒćŒ";
// Formatting Actions
diff --git a/Riot/Generated/Vector_Strings.swift b/Riot/Generated/Vector_Strings.swift
index 3c72cbcaa4..ba4a20c2f5 100644
--- a/Riot/Generated/Vector_Strings.swift
+++ b/Riot/Generated/Vector_Strings.swift
@@ -9607,7 +9607,7 @@ public class VectorL10n: NSObject {
public static var wysiwygComposerFormatActionQuote: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_quote")
}
- /// Apply underline format
+ /// Apply strikethrough format
public static var wysiwygComposerFormatActionStrikethrough: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_strikethrough")
}
@@ -9615,7 +9615,7 @@ public class VectorL10n: NSObject {
public static var wysiwygComposerFormatActionUnIndent: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_un_indent")
}
- /// Apply strikethrough format
+ /// Apply underline format
public static var wysiwygComposerFormatActionUnderline: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_underline")
}
diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift
index a3bee4c8f3..994e4fb2c6 100644
--- a/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift
+++ b/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift
@@ -23,13 +23,13 @@ final class HomeserverConfiguration: NSObject {
// Note: Use an object per configuration subject when there is multiple properties related
let jitsi: HomeserverJitsiConfiguration
let encryption: HomeserverEncryptionConfiguration
-// let tileServer: HomeserverTileServerConfiguration
+ let tileServer: HomeserverTileServerConfiguration
init(jitsi: HomeserverJitsiConfiguration,
- encryption: HomeserverEncryptionConfiguration/*,
- tileServer: HomeserverTileServerConfiguration*/) {
+ encryption: HomeserverEncryptionConfiguration,
+ tileServer: HomeserverTileServerConfiguration) {
self.jitsi = jitsi
self.encryption = encryption
-// self.tileServer = tileServer
+ self.tileServer = tileServer
}
}
diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift
index 494b50d0a0..bc6182faac 100644
--- a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift
+++ b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift
@@ -78,15 +78,15 @@ final class HomeserverConfigurationBuilder: NSObject {
// Tile server configuration
-// let tileServerMapStyleURL: URL
-// if let mapStyleURLString = wellKnown?.tileServer?.mapStyleURLString,
-// let mapStyleURL = URL(string: mapStyleURLString) {
-// tileServerMapStyleURL = mapStyleURL
-// } else {
-// tileServerMapStyleURL = BuildSettings.defaultTileServerMapStyleURL
-// }
-//
-// let tileServerConfiguration = HomeserverTileServerConfiguration(mapStyleURL: tileServerMapStyleURL)
+ let tileServerMapStyleURL: URL
+ if let mapStyleURLString = wellKnown?.tileServer?.mapStyleURLString,
+ let mapStyleURL = URL(string: mapStyleURLString) {
+ tileServerMapStyleURL = mapStyleURL
+ } else {
+ tileServerMapStyleURL = BuildSettings.defaultTileServerMapStyleURL
+ }
+
+ let tileServerConfiguration = HomeserverTileServerConfiguration(mapStyleURL: tileServerMapStyleURL)
// Create HomeserverConfiguration
@@ -95,8 +95,8 @@ final class HomeserverConfigurationBuilder: NSObject {
useFor1To1Calls: useJitsiFor1To1Calls)
return HomeserverConfiguration(jitsi: jitsiConfiguration,
- encryption: encryptionConfiguration/*,
- tileServer: tileServerConfiguration*/)
+ encryption: encryptionConfiguration,
+ tileServer: tileServerConfiguration)
}
// MARK: - Private
diff --git a/Riot/Modules/Analytics/Analytics.swift b/Riot/Modules/Analytics/Analytics.swift
index 1a30841b98..d9d68e2f85 100644
--- a/Riot/Modules/Analytics/Analytics.swift
+++ b/Riot/Modules/Analytics/Analytics.swift
@@ -213,6 +213,25 @@ import AnalyticsEvents
}
}
+@objc
+protocol E2EAnalytics {
+ func trackE2EEError(_ failure: DecryptionFailure)
+}
+
+
+@objc extension Analytics: E2EAnalytics {
+
+ /// Track an E2EE error that occurred
+ /// - Parameters:
+ /// - reason: The error that occurred.
+ /// - context: Additional context of the error that occured
+ func trackE2EEError(_ failure: DecryptionFailure) {
+ let event = failure.toAnalyticsEvent()
+ capture(event: event)
+ }
+
+}
+
// MARK: - Public tracking methods
// The following methods are exposed for compatibility with Objective-C as
// the `capture` method and the generated events cannot be bridged from Swift.
@@ -266,20 +285,7 @@ extension Analytics {
func trackInteraction(_ uiElement: AnalyticsUIElement) {
trackInteraction(uiElement, interactionType: .Touch, index: nil)
}
-
- /// Track an E2EE error that occurred
- /// - Parameters:
- /// - reason: The error that occurred.
- /// - context: Additional context of the error that occured
- func trackE2EEError(_ reason: DecryptionFailureReason, context: String) {
- let event = AnalyticsEvent.Error(
- context: context,
- cryptoModule: .Rust,
- domain: .E2EE,
- name: reason.errorName
- )
- capture(event: event)
- }
+
/// Track when a user becomes unauthenticated without pressing the `sign out` button.
/// - Parameters:
@@ -355,7 +361,8 @@ extension Analytics: MXAnalyticsDelegate {
func trackCallError(with reason: __MXCallHangupReason, video isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
let callEvent = AnalyticsEvent.CallError(isVideo: isVideo, numParticipants: numberOfParticipants, placed: !isIncoming)
- let event = AnalyticsEvent.Error(context: nil, cryptoModule: nil, domain: .VOIP, name: reason.errorName)
+ let event = AnalyticsEvent.Error(context: nil, cryptoModule: nil, cryptoSDK: nil, domain: .VOIP, eventLocalAgeMillis: nil,
+ isFederated: nil, isMatrixDotOrg: nil, name: reason.errorName, timeToDecryptMillis: nil, userTrustsOwnIdentity: nil, wasVisibleToUser: nil)
capture(event: callEvent)
capture(event: event)
}
@@ -386,6 +393,7 @@ extension Analytics: MXAnalyticsDelegate {
let event = AnalyticsEvent.Composer(inThread: inThread,
isEditing: isEditing,
isReply: isReply,
+ messageType: .Text,
startsThread: startsThread)
capture(event: event)
}
diff --git a/Riot/Modules/Analytics/DecryptionFailure+Analytics.swift b/Riot/Modules/Analytics/DecryptionFailure+Analytics.swift
new file mode 100644
index 0000000000..cd129aee35
--- /dev/null
+++ b/Riot/Modules/Analytics/DecryptionFailure+Analytics.swift
@@ -0,0 +1,44 @@
+//
+// Copyright 2024 New Vector Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+import AnalyticsEvents
+
+extension DecryptionFailure {
+
+ public func toAnalyticsEvent() -> AnalyticsEvent.Error {
+
+ let timeToDecryptMillis: Int = if self.timeToDecrypt != nil {
+ Int(self.timeToDecrypt! * 1000)
+ } else {
+ -1
+ }
+ return AnalyticsEvent.Error(
+ context: self.context,
+ cryptoModule: .Rust,
+ cryptoSDK: .Rust,
+ domain: .E2EE,
+
+ eventLocalAgeMillis: nil,
+ isFederated: nil,
+ isMatrixDotOrg: nil,
+ name: self.reason.errorName,
+ timeToDecryptMillis: timeToDecryptMillis,
+ userTrustsOwnIdentity: nil,
+ wasVisibleToUser: nil
+ )
+ }
+}
diff --git a/Riot/Modules/Analytics/DecryptionFailure.swift b/Riot/Modules/Analytics/DecryptionFailure.swift
index 1c991db88f..9e0ca57858 100644
--- a/Riot/Modules/Analytics/DecryptionFailure.swift
+++ b/Riot/Modules/Analytics/DecryptionFailure.swift
@@ -38,15 +38,19 @@ import AnalyticsEvents
/// The id of the event that was unabled to decrypt.
let failedEventId: String
/// The time the failure has been reported.
- let ts: TimeInterval = Date().timeIntervalSince1970
+ let ts: TimeInterval
/// Decryption failure reason.
let reason: DecryptionFailureReason
/// Additional context of failure
let context: String
- init(failedEventId: String, reason: DecryptionFailureReason, context: String) {
+ /// UTDs can be permanent or temporary. If temporary, this field will contain the time it took to decrypt the message in milliseconds. If permanent should be nil
+ var timeToDecrypt: TimeInterval?
+
+ init(failedEventId: String, reason: DecryptionFailureReason, context: String, ts: TimeInterval) {
self.failedEventId = failedEventId
self.reason = reason
self.context = context
+ self.ts = ts
}
}
diff --git a/Riot/Modules/Analytics/DecryptionFailureTracker.h b/Riot/Modules/Analytics/DecryptionFailureTracker.h
deleted file mode 100644
index b8f9ca467e..0000000000
--- a/Riot/Modules/Analytics/DecryptionFailureTracker.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- Copyright 2018 New Vector Ltd
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-#import
-
-@class DecryptionFailureTracker;
-
-@class Analytics;
-@import MatrixSDK;
-
-@interface DecryptionFailureTracker : NSObject
-
-/**
- Returns the shared tracker.
-
- @return the shared tracker.
- */
-+ (instancetype)sharedInstance;
-
-/**
- The delegate object to receive analytics events.
- */
-@property (nonatomic, weak) Analytics *delegate;
-
-/**
- Report an event unable to decrypt.
-
- This error can be momentary. The DecryptionFailureTracker will check if it gets
- fixed. Else, it will generate a failure (@see `trackFailures`).
-
- @param event the event.
- @param roomState the room state when the event was received.
- @param userId my user id.
- */
-- (void)reportUnableToDecryptErrorForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState myUser:(NSString*)userId;
-
-/**
- Flush current data.
- */
-- (void)dispatch;
-
-@end
diff --git a/Riot/Modules/Analytics/DecryptionFailureTracker.m b/Riot/Modules/Analytics/DecryptionFailureTracker.m
deleted file mode 100644
index 4a749b71aa..0000000000
--- a/Riot/Modules/Analytics/DecryptionFailureTracker.m
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- Copyright 2018 New Vector Ltd
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-#import "DecryptionFailureTracker.h"
-#import "GeneratedInterface-Swift.h"
-
-
-// Call `checkFailures` every `CHECK_INTERVAL`
-#define CHECK_INTERVAL 2
-
-// Give events a chance to be decrypted by waiting `GRACE_PERIOD` before counting
-// and reporting them as failures
-#define GRACE_PERIOD 4
-
-// E2E failures analytics category.
-NSString *const kDecryptionFailureTrackerAnalyticsCategory = @"e2e.failure";
-
-@interface DecryptionFailureTracker()
-{
- // Reported failures
- // Every `CHECK_INTERVAL`, this list is checked for failures that happened
- // more than`GRACE_PERIOD` ago. Those that did are reported to the delegate.
- NSMutableDictionary *reportedFailures;
-
- // Event ids of failures that were tracked previously
- NSMutableSet *trackedEvents;
-
- // Timer for periodic check
- NSTimer *checkFailuresTimer;
-}
-@end
-
-@implementation DecryptionFailureTracker
-
-+ (instancetype)sharedInstance
-{
- static DecryptionFailureTracker *sharedInstance = nil;
- static dispatch_once_t onceToken;
-
- dispatch_once(&onceToken, ^{
- sharedInstance = [[DecryptionFailureTracker alloc] init];
- });
-
- return sharedInstance;
-}
-
-- (instancetype)init
-{
- self = [super init];
- if (self)
- {
- reportedFailures = [NSMutableDictionary dictionary];
- trackedEvents = [NSMutableSet set];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidDecrypt:) name:kMXEventDidDecryptNotification object:nil];
-
- checkFailuresTimer = [NSTimer scheduledTimerWithTimeInterval:CHECK_INTERVAL
- target:self
- selector:@selector(checkFailures)
- userInfo:nil
- repeats:YES];
- }
- return self;
-}
-
-- (void)reportUnableToDecryptErrorForEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState myUser:(NSString *)userId
-{
- if (reportedFailures[event.eventId] || [trackedEvents containsObject:event.eventId])
- {
- return;
- }
-
- // Filter out "expected" UTDs
- // We cannot decrypt messages sent before the user joined the room
- MXRoomMember *myUser = [roomState.members memberWithUserId:userId];
- if (!myUser || myUser.membership != MXMembershipJoin)
- {
- return;
- }
-
- NSString *failedEventId = event.eventId;
- DecryptionFailureReason reason;
-
- // Categorise the error
- switch (event.decryptionError.code)
- {
- case MXDecryptingErrorUnknownInboundSessionIdCode:
- reason = DecryptionFailureReasonOlmKeysNotSent;
- break;
-
- case MXDecryptingErrorOlmCode:
- reason = DecryptionFailureReasonOlmIndexError;
- break;
-
- default:
- // All other error codes will be tracked as `OlmUnspecifiedError` and will include `context` containing
- // the actual error code and localized description
- reason = DecryptionFailureReasonUnspecified;
- break;
- }
-
- NSString *context = [NSString stringWithFormat:@"code: %ld, description: %@", event.decryptionError.code, event.decryptionError.localizedDescription];
- reportedFailures[event.eventId] = [[DecryptionFailure alloc] initWithFailedEventId:failedEventId
- reason:reason
- context:context];
-}
-
-- (void)dispatch
-{
- [self checkFailures];
-}
-
-#pragma mark - Private methods
-
-/**
- Mark reported failures that occured before tsNow - GRACE_PERIOD as failures that should be
- tracked.
- */
-- (void)checkFailures
-{
- if (!_delegate)
- {
- return;
- }
-
- NSTimeInterval tsNow = [NSDate date].timeIntervalSince1970;
-
- NSMutableArray *failuresToTrack = [NSMutableArray array];
-
- for (DecryptionFailure *reportedFailure in reportedFailures.allValues)
- {
- if (reportedFailure.ts < tsNow - GRACE_PERIOD)
- {
- [failuresToTrack addObject:reportedFailure];
- [reportedFailures removeObjectForKey:reportedFailure.failedEventId];
- [trackedEvents addObject:reportedFailure.failedEventId];
- }
- }
-
- if (failuresToTrack.count)
- {
- // Sort failures by error reason
- NSMutableDictionary *failuresCounts = [NSMutableDictionary dictionary];
- for (DecryptionFailure *failure in failuresToTrack)
- {
- failuresCounts[@(failure.reason)] = @(failuresCounts[@(failure.reason)].unsignedIntegerValue + 1);
- [self.delegate trackE2EEError:failure.reason context:failure.context];
- }
-
- MXLogDebug(@"[DecryptionFailureTracker] trackFailures: %@", failuresCounts);
- }
-}
-
-- (void)eventDidDecrypt:(NSNotification *)notif
-{
- // Could be an event in the reportedFailures, remove it
- MXEvent *event = notif.object;
- [reportedFailures removeObjectForKey:event.eventId];
-}
-
-@end
diff --git a/Riot/Modules/Analytics/DecryptionFailureTracker.swift b/Riot/Modules/Analytics/DecryptionFailureTracker.swift
new file mode 100644
index 0000000000..19b8afb19c
--- /dev/null
+++ b/Riot/Modules/Analytics/DecryptionFailureTracker.swift
@@ -0,0 +1,174 @@
+//
+// Copyright 2024 New Vector Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+
+// Protocol to get the current time. Used for easy testing
+protocol TimeProvider {
+ func nowTs() -> TimeInterval
+}
+
+class DefaultTimeProvider: TimeProvider {
+
+ func nowTs() -> TimeInterval {
+ return Date.now.timeIntervalSince1970
+ }
+
+}
+
+
+@objc
+class DecryptionFailureTracker: NSObject {
+
+ let GRACE_PERIOD: TimeInterval = 4
+ // Call `checkFailures` every `CHECK_INTERVAL`
+ let CHECK_INTERVAL: TimeInterval = 15
+
+ // The maximum time to wait for a late decryption before reporting as permanent UTD
+ let MAX_WAIT_FOR_LATE_DECRYPTION: TimeInterval = 60
+
+ @objc weak var delegate: E2EAnalytics?
+
+ // Reported failures
+ var reportedFailures = [String /* eventId */: DecryptionFailure]()
+
+ // Event ids of failures that were tracked previously
+ var trackedEvents = Set()
+
+ var checkFailuresTimer: Timer?
+
+ @objc static let sharedInstance = DecryptionFailureTracker()
+
+ var timeProvider: TimeProvider = DefaultTimeProvider()
+
+ override init() {
+ super.init()
+
+ NotificationCenter.default.addObserver(self,
+ selector: #selector(eventDidDecrypt(_:)),
+ name: .mxEventDidDecrypt,
+ object: nil)
+
+ }
+
+ @objc
+ func reportUnableToDecryptError(forEvent event: MXEvent, withRoomState roomState: MXRoomState, myUser userId: String) {
+ if reportedFailures[event.eventId] != nil || trackedEvents.contains(event.eventId) {
+ return
+ }
+
+ // Filter out "expected" UTDs
+ // We cannot decrypt messages sent before the user joined the room
+ guard let myUser = roomState.members.member(withUserId: userId) else { return }
+ if myUser.membership != MXMembership.join {
+ return
+ }
+
+ guard let failedEventId = event.eventId else { return }
+
+ guard let error = event.decryptionError as? NSError else { return }
+
+ var reason = DecryptionFailureReason.unspecified
+
+ if error.code == MXDecryptingErrorUnknownInboundSessionIdCode.rawValue {
+ reason = DecryptionFailureReason.olmKeysNotSent
+ } else if error.code == MXDecryptingErrorOlmCode.rawValue {
+ reason = DecryptionFailureReason.olmIndexError
+ }
+
+ let context = String(format: "code: %ld, description: %@", error.code, event.decryptionError.localizedDescription)
+
+ reportedFailures[failedEventId] = DecryptionFailure(failedEventId: failedEventId, reason: reason, context: context, ts: self.timeProvider.nowTs())
+
+ // Start the ticker if needed. There is no need to have a ticker if no failures are tracked
+ if checkFailuresTimer == nil {
+ self.checkFailuresTimer = Timer.scheduledTimer(withTimeInterval: CHECK_INTERVAL, repeats: true) { [weak self] _ in
+ self?.checkFailures()
+ }
+ }
+
+ }
+
+ @objc
+ func dispatch() {
+ self.checkFailures()
+ }
+
+ @objc
+ func eventDidDecrypt(_ notification: Notification) {
+ guard let event = notification.object as? MXEvent else { return }
+
+ guard let reportedFailure = self.reportedFailures[event.eventId] else { return }
+
+ let now = self.timeProvider.nowTs()
+ let ellapsedTime = now - reportedFailure.ts
+
+ if ellapsedTime < 4 {
+ // event is graced
+ reportedFailures.removeValue(forKey: event.eventId)
+ } else {
+ // It's a late decrypt must be reported as a late decrypt
+ reportedFailure.timeToDecrypt = ellapsedTime
+ self.delegate?.trackE2EEError(reportedFailure)
+ }
+ // Remove from reported failures
+ self.trackedEvents.insert(event.eventId)
+ reportedFailures.removeValue(forKey: event.eventId)
+
+ // Check if we still need the ticker timer
+ if reportedFailures.isEmpty {
+ // Invalidate the current timer, nothing to check for
+ self.checkFailuresTimer?.invalidate()
+ self.checkFailuresTimer = nil
+ }
+
+ }
+
+ /**
+ Mark reported failures that occured before tsNow - GRACE_PERIOD as failures that should be
+ tracked.
+ */
+ @objc
+ func checkFailures() {
+ guard let delegate = self.delegate else {return}
+
+ let tsNow = self.timeProvider.nowTs()
+ var failuresToCheck = [DecryptionFailure]()
+
+ for reportedFailure in self.reportedFailures.values {
+ let ellapsed = tsNow - reportedFailure.ts
+ if ellapsed > MAX_WAIT_FOR_LATE_DECRYPTION {
+ failuresToCheck.append(reportedFailure)
+ reportedFailure.timeToDecrypt = nil
+ reportedFailures.removeValue(forKey: reportedFailure.failedEventId)
+ trackedEvents.insert(reportedFailure.failedEventId)
+ }
+ }
+
+ for failure in failuresToCheck {
+ delegate.trackE2EEError(failure)
+ }
+
+ // Check if we still need the ticker timer
+ if reportedFailures.isEmpty {
+ // Invalidate the current timer, nothing to check for
+ self.checkFailuresTimer?.invalidate()
+ self.checkFailuresTimer = nil
+ }
+ }
+
+}
diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift
index 2993942d42..e55874016c 100755
--- a/Riot/Modules/Application/AppCoordinator.swift
+++ b/Riot/Modules/Application/AppCoordinator.swift
@@ -442,14 +442,17 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
self.expiredAccountAlertController?.dismiss(animated: false)
- let alert = UIAlertController(title: TchapL10n.warningTitle, message: TchapL10n.expiredAccountAlertMessage, preferredStyle: .alert)
+ // Tchap: customize wording
+ let alert = UIAlertController(title: TchapL10n.expiredAccountAlertTitle, message: TchapL10n.expiredAccountAlertMessage, preferredStyle: .alert)
+ // Tchap: customize wording
let resumeTitle = TchapL10n.expiredAccountResumeButton
let resumeAction = UIAlertAction(title: resumeTitle, style: .default, handler: { action in
// Relaunch the session
self.reloadSession(clearCache: false)
})
alert.addAction(resumeAction)
+ // Tchap: customize wording
let sendEmailTitle = TchapL10n.expiredAccountRequestRenewalEmailButton
let sendEmailAction = UIAlertAction(title: sendEmailTitle, style: .default, handler: { action in
// Request a new email for the main account
@@ -484,9 +487,10 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
self.expiredAccountAlertController?.dismiss(animated: false)
- let alert = UIAlertController(title: TchapL10n.infoTitle, message: TchapL10n.expiredAccountOnNewSentEmailMsg, preferredStyle: .alert)
+ // Tchap: customize wording
+ let alert = UIAlertController(title: TchapL10n.expiredAccountOnNewSentEmailTitle, message: TchapL10n.expiredAccountOnNewSentEmailMessage, preferredStyle: .alert)
- let resumeTitle = TchapL10n.expiredAccountResumeButton
+ let resumeTitle = TchapL10n.expiredAccountOnNewSentEmailButton
let resumeAction = UIAlertAction(title: resumeTitle, style: .default, handler: { action in
// Relaunch the session
self.reloadSession(clearCache: false)
diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m
index db266e53f0..1011a4f832 100644
--- a/Riot/Modules/Application/LegacyAppDelegate.m
+++ b/Riot/Modules/Application/LegacyAppDelegate.m
@@ -33,7 +33,6 @@
//#import "ContactDetailsViewController.h"
#import "BugReportViewController.h"
-#import "DecryptionFailureTracker.h"
#import "Tools.h"
#import "WidgetManager.h"
diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
index 96d0025cec..48add73d5f 100644
--- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
+++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
@@ -255,10 +255,13 @@ - (RecentsDataSourceSections *)makeDataSourceSections
[types addObject:@(RecentsDataSourceSectionTypeLowPriority)];
}
- if (self.serverNoticeCellDataArray.count > 0)
- {
- [types addObject:@(RecentsDataSourceSectionTypeServerNotice)];
- }
+ // Tchap: don't display server notices as separate section.
+ // Server notices are displayed in the main section "all chats"
+ // (see modification in `updateConversationFetcher` in `RecentListService`
+// if (self.serverNoticeCellDataArray.count > 0)
+// {
+// [types addObject:@(RecentsDataSourceSectionTypeServerNotice)];
+// }
return [[RecentsDataSourceSections alloc] initWithSectionTypes:types.copy];
}
diff --git a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift
index bd8f52ae77..1401abe6c4 100644
--- a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift
+++ b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift
@@ -717,7 +717,12 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
}
private func updateConversationFetcher(_ fetcher: MXRoomListDataFetcher, for mode: RecentsDataSourceMode) {
- var notDataTypes: MXRoomSummaryDataTypes = mode == .allChats ? [.hidden, .conferenceUser, .invited, .lowPriority, .serverNotice, .space] : [.hidden, .conferenceUser, .direct, .invited, .lowPriority, .serverNotice, .space]
+ // Tchap:
+ // don't exclude serverNotice channels from "all chats" group fetcher
+ // to get server Notices (aka "Tchap Annonces") in the main room list
+ // to be displayed as classic rooms
+// var notDataTypes: MXRoomSummaryDataTypes = mode == .allChats ? [.hidden, .conferenceUser, .invited, .lowPriority, .serverNotice, .space] : [.hidden, .conferenceUser, .direct, .invited, .lowPriority, .serverNotice, .space]
+ var notDataTypes: MXRoomSummaryDataTypes = mode == .allChats ? [.hidden, .conferenceUser, .invited, .lowPriority, /*.serverNotice,*/ .space] : [.hidden, .conferenceUser, .direct, .invited, .lowPriority, /*.serverNotice,*/ .space]
switch mode {
case .home:
diff --git a/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m b/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m
index 4e9dedb143..32dd25ab37 100644
--- a/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m
+++ b/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m
@@ -93,13 +93,7 @@ - (void)render:(MXKCellData *)cellData
self.lastEventDecriptionLabelTrailingConstraint.constant = self.unsentImageView.hidden ? 10 : 30;
// Notify unreads and bing
- if (roomCellData.isRoomMarkedAsUnread)
- {
- self.missedNotifAndUnreadBadgeBgView.hidden = NO;
- self.missedNotifAndUnreadBadgeBgView.backgroundColor = ThemeService.shared.theme.tintColor;
- self.missedNotifAndUnreadBadgeBgViewWidthConstraint.constant = 20;
- }
- else if (roomCellData.hasUnread)
+ if (roomCellData.hasUnread)
{
self.missedNotifAndUnreadIndicator.hidden = NO;
if (0 < roomCellData.notificationCount)
diff --git a/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift b/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift
index c019aae9c0..d9cde9dbe6 100644
--- a/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift
+++ b/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift
@@ -34,7 +34,7 @@ class RoomActionProvider: RoomActionProviderProtocol {
var menu: UIMenu {
if service.isRoomJoined {
- var children = service.hasUnread ? [self.markAsReadAction] : [self.markAsUnreadAction]
+ var children = service.hasUnread ? [self.markAsReadAction] : []
children.append(contentsOf: [
self.directChatAction,
self.notificationsAction,
diff --git a/Riot/Modules/ContextMenu/Services/RoomContextActionService.swift b/Riot/Modules/ContextMenu/Services/RoomContextActionService.swift
index 12c02c9381..eb49328250 100644
--- a/Riot/Modules/ContextMenu/Services/RoomContextActionService.swift
+++ b/Riot/Modules/ContextMenu/Services/RoomContextActionService.swift
@@ -38,7 +38,7 @@ class RoomContextActionService: NSObject, RoomContextActionServiceProtocol {
self.room = room
self.delegate = delegate
self.isRoomJoined = room.summary?.isJoined ?? false
- self.hasUnread = (room.summary?.hasAnyUnread ?? false) || room.isMarkedAsUnread
+ self.hasUnread = room.summary?.hasAnyUnread ?? false
self.roomMembership = room.summary?.membership ?? .unknown
self.session = room.mxSession
self.unownedRoomService = UnownedRoomContextActionService(roomId: room.roomId, canonicalAlias: room.summary?.aliases?.first, session: self.session, delegate: delegate)
diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m
index b67c5b78b6..8b297e221e 100644
--- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m
+++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m
@@ -998,7 +998,11 @@ -(void)openSessionWithStore:(id)store
// Enable the antivirus scanner in the current session.
[mxSession setAntivirusServerURL:_antivirusServerURL];
}
-
+ // Tchap: else hard code antivirus server URL with homeServer URL like in Tchap Android.
+ else {
+ [mxSession setAntivirusServerURL:self.mxCredentials.homeServer];
+ }
+
// Set default MXEvent -> NSString formatter
MXKEventFormatter *eventFormatter = [[MXKEventFormatter alloc] initWithMatrixSession:self.mxSession];
eventFormatter.isForSubtitle = YES;
diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m
index d6a3b5cfd3..1fd543a036 100644
--- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m
+++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m
@@ -2029,6 +2029,9 @@ - (NSString*)renderReplyTo:(NSString*)htmlString withRoomState:(MXRoomState*)roo
}
}
+ // Tchap: truncate quoted reply if necessary.
+ html = [self tchapTruncatedQuotedReplyFrom:html];
+
// Replace In reply to
// By ['In reply to' from resources]
// To localize the "In reply to" string
@@ -2042,6 +2045,42 @@ - (NSString*)renderReplyTo:(NSString*)htmlString withRoomState:(MXRoomState*)roo
return html;
}
+// Tchap: truncate long quoted reply
+- (NSString *)tchapTruncatedQuotedReplyFrom:(NSString *)fullQuotedReply {
+ static NSRegularExpression *htmlQuotedTextRegex;
+
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ htmlQuotedTextRegex = [NSRegularExpression regularExpressionWithPattern:kRepliedTextPattern
+ options:NSRegularExpressionCaseInsensitive
+ error:nil];
+ });
+
+ NSTextCheckingResult * regexResults = [htmlQuotedTextRegex firstMatchInString:fullQuotedReply
+ options:0
+ range:NSMakeRange(0, fullQuotedReply.length)];
+
+ // Check if a quoted text is present.
+ if( regexResults.numberOfRanges < 1 )
+ {
+ // No reply found.
+ return fullQuotedReply;
+ }
+
+ NSRange quotedTextRange = [regexResults rangeAtIndex:1];
+
+ NSUInteger quotedTextMaxLength = 60; // Max length of quoted text
+
+ if( quotedTextRange.location != NSNotFound && quotedTextRange.length > quotedTextMaxLength )
+ {
+ NSRange truncatedRange = NSMakeRange(quotedTextRange.location + quotedTextMaxLength, quotedTextRange.length - quotedTextMaxLength);
+ return [fullQuotedReply stringByReplacingCharactersInRange:truncatedRange withString:@"âŠ"];
+ }
+
+ // The quoted reply is not found or already short. Return it as is.
+ return fullQuotedReply;
+}
+
- (NSString*)renderPollEndedReplyTo:(NSString*)htmlString repliedEvent:(MXEvent*)repliedEvent {
static NSRegularExpression *endedPollRegex;
static dispatch_once_t onceToken;
diff --git a/Riot/Modules/QRCode/Reader/QRCodeReaderView.swift b/Riot/Modules/QRCode/Reader/QRCodeReaderView.swift
index 21f1b8497c..302cd63104 100644
--- a/Riot/Modules/QRCode/Reader/QRCodeReaderView.swift
+++ b/Riot/Modules/QRCode/Reader/QRCodeReaderView.swift
@@ -113,7 +113,6 @@ final class QRCodeReaderView: UIView {
}
private func applyOrientation() {
-
let orientation = UIApplication.shared.statusBarOrientation
let captureRotation: Double
let scanRectRotation: Double
@@ -136,59 +135,13 @@ final class QRCodeReaderView: UIView {
scanRectRotation = 90
}
- applyRectOfInterest(orientation: orientation)
-
let angleRadius = captureRotation / 180.0 * Double.pi
- let captureTranform = CGAffineTransform(rotationAngle: CGFloat(angleRadius))
+ let captureTransform = CGAffineTransform(rotationAngle: CGFloat(angleRadius))
- zxCapture.transform = captureTranform
+ zxCapture.transform = captureTransform
zxCapture.rotation = CGFloat(scanRectRotation)
zxCapture.layer.frame = self.bounds
}
-
- private func applyRectOfInterest(orientation: UIInterfaceOrientation) {
- var transformedVideoRect = self.frame
- let cameraSessionPreset = zxCapture.sessionPreset
-
- var scaleVideoX, scaleVideoY: CGFloat
- var videoHeight, videoWidth: CGFloat
-
- // Currently support only for 1920x1080 || 1280x720
- if cameraSessionPreset == AVCaptureSession.Preset.hd1920x1080.rawValue {
- videoHeight = 1080.0
- videoWidth = 1920.0
- } else {
- videoHeight = 720.0
- videoWidth = 1280.0
- }
-
- if orientation == UIInterfaceOrientation.portrait {
- scaleVideoX = self.frame.width / videoHeight
- scaleVideoY = self.frame.height / videoWidth
-
- // Convert CGPoint under portrait mode to map with orientation of image
- // because the image will be cropped before rotate
- // reference: https://github.com/TheLevelUp/ZXingObjC/issues/222
- let realX = transformedVideoRect.origin.y
- let realY = self.frame.size.width - transformedVideoRect.size.width - transformedVideoRect.origin.x
- let realWidth = transformedVideoRect.size.height
- let realHeight = transformedVideoRect.size.width
- transformedVideoRect = CGRect(x: realX, y: realY, width: realWidth, height: realHeight)
-
- } else {
- scaleVideoX = self.frame.width / videoWidth
- scaleVideoY = self.frame.height / videoHeight
- }
-
- captureSizeTransform = CGAffineTransform(scaleX: 1.0/scaleVideoX, y: 1.0/scaleVideoY)
-
- guard let _captureSizeTransform = captureSizeTransform else {
- return
- }
-
- let transformRect = transformedVideoRect.applying(_captureSizeTransform)
- zxCapture.scanRect = transformRect
- }
}
diff --git a/Riot/Modules/Room/RoomCoordinator.swift b/Riot/Modules/Room/RoomCoordinator.swift
index c94cdfb178..40dede1629 100644
--- a/Riot/Modules/Room/RoomCoordinator.swift
+++ b/Riot/Modules/Room/RoomCoordinator.swift
@@ -315,166 +315,166 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
}
// Tchap: Disable Live location sharing
-// private func showLiveLocationViewer() {
-// guard let roomId = self.roomId else {
-// return
-// }
-//
-// self.showLiveLocationViewer(for: roomId)
-// }
-//
-// private func showLiveLocationViewer(for roomId: String) {
-//
-// guard let mxSession = self.mxSession, let navigationRouter = self.navigationRouter else {
-// return
-// }
-//
-// guard mxSession.locationService.isSomeoneSharingDisplayableLocation(inRoomWithId: roomId) else {
-// return
-// }
-//
-// let parameters = LiveLocationSharingViewerCoordinatorParameters(session: mxSession, roomId: roomId, navigationRouter: nil)
-//
-// let coordinator = LiveLocationSharingViewerCoordinator(parameters: parameters)
-//
-// coordinator.completion = { [weak self, weak coordinator] in
-// guard let self = self, let coordinator = coordinator else {
-// return
-// }
-//
-// self.navigationRouter?.dismissModule(animated: true, completion: nil)
-// self.remove(childCoordinator: coordinator)
-// }
-//
-// add(childCoordinator: coordinator)
-//
-// navigationRouter.present(coordinator, animated: true)
-// coordinator.start()
-// }
-//
-// private func stopLiveLocationSharing(forBeaconInfoEventId beaconInfoEventId: String? = nil, inRoomWithId roomId: String) {
-// guard let session = self.mxSession else {
-// return
-// }
-//
-// let errorHandler: (Error) -> Void = { error in
-//
-// let viewController = self.roomViewController
-//
-// viewController.errorPresenter.presentError(from: viewController, title: VectorL10n.error, message: VectorL10n.locationSharingLiveStopSharingError, animated: true) {
-// }
-// }
-//
-// // TODO: Handle loading state on the banner by replacing stop button with a spinner
-// self.showLocationSharingIndicator(withMessage: VectorL10n.locationSharingLiveStopSharingProgress)
-//
-// if let beaconInfoEventId = beaconInfoEventId {
-// session.locationService.stopUserLocationSharing(withBeaconInfoEventId: beaconInfoEventId, roomId: roomId) {
-// [weak self] response in
-//
-// self?.hideLocationSharingIndicator()
-//
-// switch response {
-// case .success:
-// break
-// case .failure(let error):
-// errorHandler(error)
-// }
-// }
-// } else {
-// session.locationService.stopUserLocationSharing(inRoomWithId: roomId) { [weak self] response in
-//
-// self?.hideLocationSharingIndicator()
-//
-// switch response {
-// case .success:
-// break
-// case .failure(let error):
-// errorHandler(error)
-// }
-// }
-// }
-// }
-//
-// private func showLocationCoordinatorWithEvent(_ event: MXEvent, bubbleData: MXKRoomBubbleCellDataStoring) {
-// guard let mxSession = self.mxSession,
-// let navigationRouter = self.navigationRouter,
-// let mediaManager = mxSession.mediaManager,
-// let locationContent = event.location else {
-// MXLog.error("[RoomCoordinator] Invalid location showing coordinator parameters. Returning.")
-// return
-// }
-//
-// let avatarData = AvatarInput(mxContentUri: bubbleData.senderAvatarUrl,
-// matrixItemId: bubbleData.senderId,
-// displayName: bubbleData.senderDisplayName)
-//
-//
-// let location = CLLocationCoordinate2D(latitude: locationContent.latitude, longitude: locationContent.longitude)
-// let coordinateType = locationContent.assetType
-//
-// guard let locationSharingCoordinatetype = coordinateType.locationSharingCoordinateType() else {
-// fatalError("[LocationSharingCoordinator] event asset type is not supported: \(coordinateType)")
-// }
-//
-// let parameters = StaticLocationViewingCoordinatorParameters(
-// session: mxSession,
-// mediaManager: mediaManager,
-// avatarData: avatarData,
-// location: location,
-// coordinateType: locationSharingCoordinatetype)
-//
-// let coordinator = StaticLocationViewingCoordinator(parameters: parameters)
-//
-// coordinator.completion = { [weak self, weak coordinator] in
-// guard let self = self, let coordinator = coordinator else {
-// return
-// }
-//
-// self.navigationRouter?.dismissModule(animated: true, completion: nil)
-// self.remove(childCoordinator: coordinator)
-// }
-//
-// add(childCoordinator: coordinator)
-//
-// navigationRouter.present(coordinator, animated: true)
-// coordinator.start()
-// }
-//
-// private func startLocationCoordinator() {
-// guard let mxSession = mxSession,
-// let navigationRouter = self.navigationRouter,
-// let mediaManager = mxSession.mediaManager,
-// let user = mxSession.myUser else {
-// MXLog.error("[RoomCoordinator] Invalid location sharing coordinator parameters. Returning.")
-// return
-// }
-//
-// let avatarData = AvatarInput(mxContentUri: user.avatarUrl,
-// matrixItemId: user.userId,
-// displayName: user.displayname)
-//
-// let parameters = LocationSharingCoordinatorParameters(session: mxSession,
-// roomDataSource: roomViewController.roomDataSource,
-// mediaManager: mediaManager,
-// avatarData: avatarData)
-//
-// let coordinator = LocationSharingCoordinator(parameters: parameters)
-//
-// coordinator.completion = { [weak self, weak coordinator] in
-// guard let self = self, let coordinator = coordinator else {
-// return
-// }
-//
-// self.navigationRouter?.dismissModule(animated: true, completion: nil)
-// self.remove(childCoordinator: coordinator)
-// }
-//
-// add(childCoordinator: coordinator)
-//
-// navigationRouter.present(coordinator, animated: true)
-// coordinator.start()
-// }
+ private func showLiveLocationViewer() {
+ guard let roomId = self.roomId else {
+ return
+ }
+
+ self.showLiveLocationViewer(for: roomId)
+ }
+
+ private func showLiveLocationViewer(for roomId: String) {
+
+ guard let mxSession = self.mxSession, let navigationRouter = self.navigationRouter else {
+ return
+ }
+
+ guard mxSession.locationService.isSomeoneSharingDisplayableLocation(inRoomWithId: roomId) else {
+ return
+ }
+
+ let parameters = LiveLocationSharingViewerCoordinatorParameters(session: mxSession, roomId: roomId, navigationRouter: nil)
+
+ let coordinator = LiveLocationSharingViewerCoordinator(parameters: parameters)
+
+ coordinator.completion = { [weak self, weak coordinator] in
+ guard let self = self, let coordinator = coordinator else {
+ return
+ }
+
+ self.navigationRouter?.dismissModule(animated: true, completion: nil)
+ self.remove(childCoordinator: coordinator)
+ }
+
+ add(childCoordinator: coordinator)
+
+ navigationRouter.present(coordinator, animated: true)
+ coordinator.start()
+ }
+
+ private func stopLiveLocationSharing(forBeaconInfoEventId beaconInfoEventId: String? = nil, inRoomWithId roomId: String) {
+ guard let session = self.mxSession else {
+ return
+ }
+
+ let errorHandler: (Error) -> Void = { error in
+
+ let viewController = self.roomViewController
+
+ viewController.errorPresenter.presentError(from: viewController, title: VectorL10n.error, message: VectorL10n.locationSharingLiveStopSharingError, animated: true) {
+ }
+ }
+
+ // TODO: Handle loading state on the banner by replacing stop button with a spinner
+ self.showLocationSharingIndicator(withMessage: VectorL10n.locationSharingLiveStopSharingProgress)
+
+ if let beaconInfoEventId = beaconInfoEventId {
+ session.locationService.stopUserLocationSharing(withBeaconInfoEventId: beaconInfoEventId, roomId: roomId) {
+ [weak self] response in
+
+ self?.hideLocationSharingIndicator()
+
+ switch response {
+ case .success:
+ break
+ case .failure(let error):
+ errorHandler(error)
+ }
+ }
+ } else {
+ session.locationService.stopUserLocationSharing(inRoomWithId: roomId) { [weak self] response in
+
+ self?.hideLocationSharingIndicator()
+
+ switch response {
+ case .success:
+ break
+ case .failure(let error):
+ errorHandler(error)
+ }
+ }
+ }
+ }
+
+ private func showLocationCoordinatorWithEvent(_ event: MXEvent, bubbleData: MXKRoomBubbleCellDataStoring) {
+ guard let mxSession = self.mxSession,
+ let navigationRouter = self.navigationRouter,
+ let mediaManager = mxSession.mediaManager,
+ let locationContent = event.location else {
+ MXLog.error("[RoomCoordinator] Invalid location showing coordinator parameters. Returning.")
+ return
+ }
+
+ let avatarData = AvatarInput(mxContentUri: bubbleData.senderAvatarUrl,
+ matrixItemId: bubbleData.senderId,
+ displayName: bubbleData.senderDisplayName)
+
+
+ let location = CLLocationCoordinate2D(latitude: locationContent.latitude, longitude: locationContent.longitude)
+ let coordinateType = locationContent.assetType
+
+ guard let locationSharingCoordinatetype = coordinateType.locationSharingCoordinateType() else {
+ fatalError("[LocationSharingCoordinator] event asset type is not supported: \(coordinateType)")
+ }
+
+ let parameters = StaticLocationViewingCoordinatorParameters(
+ session: mxSession,
+ mediaManager: mediaManager,
+ avatarData: avatarData,
+ location: location,
+ coordinateType: locationSharingCoordinatetype)
+
+ let coordinator = StaticLocationViewingCoordinator(parameters: parameters)
+
+ coordinator.completion = { [weak self, weak coordinator] in
+ guard let self = self, let coordinator = coordinator else {
+ return
+ }
+
+ self.navigationRouter?.dismissModule(animated: true, completion: nil)
+ self.remove(childCoordinator: coordinator)
+ }
+
+ add(childCoordinator: coordinator)
+
+ navigationRouter.present(coordinator, animated: true)
+ coordinator.start()
+ }
+
+ private func startLocationCoordinator() {
+ guard let mxSession = mxSession,
+ let navigationRouter = self.navigationRouter,
+ let mediaManager = mxSession.mediaManager,
+ let user = mxSession.myUser else {
+ MXLog.error("[RoomCoordinator] Invalid location sharing coordinator parameters. Returning.")
+ return
+ }
+
+ let avatarData = AvatarInput(mxContentUri: user.avatarUrl,
+ matrixItemId: user.userId,
+ displayName: user.displayname)
+
+ let parameters = LocationSharingCoordinatorParameters(session: mxSession,
+ roomDataSource: roomViewController.roomDataSource,
+ mediaManager: mediaManager,
+ avatarData: avatarData)
+
+ let coordinator = LocationSharingCoordinator(parameters: parameters)
+
+ coordinator.completion = { [weak self, weak coordinator] in
+ guard let self = self, let coordinator = coordinator else {
+ return
+ }
+
+ self.navigationRouter?.dismissModule(animated: true, completion: nil)
+ self.remove(childCoordinator: coordinator)
+ }
+
+ add(childCoordinator: coordinator)
+
+ navigationRouter.present(coordinator, animated: true)
+ coordinator.start()
+ }
private func startEditPollCoordinator(startEvent: MXEvent? = nil) {
let parameters = PollEditFormCoordinatorParameters(room: roomViewController.roomDataSource.room, pollStartEvent: startEvent)
@@ -579,7 +579,7 @@ extension RoomCoordinator: UIAdaptivePresentationControllerDelegate {
// MARK: - RoomViewControllerDelegate
extension RoomCoordinator: RoomViewControllerDelegate {
-
+
func roomViewController(_ roomViewController: RoomViewController, showRoomWithId roomID: String, eventId eventID: String?) {
self.delegate?.roomCoordinator(self, didSelectRoomWithId: roomID, eventId: eventID)
}
@@ -621,11 +621,11 @@ extension RoomCoordinator: RoomViewControllerDelegate {
}
func roomViewControllerDidRequestLocationSharingFormPresentation(_ roomViewController: RoomViewController) {
-// startLocationCoordinator()
+ startLocationCoordinator()
}
func roomViewController(_ roomViewController: RoomViewController, didRequestLocationPresentationFor event: MXEvent, bubbleData: MXKRoomBubbleCellDataStoring) {
-// showLocationCoordinatorWithEvent(event, bubbleData: bubbleData)
+ showLocationCoordinatorWithEvent(event, bubbleData: bubbleData)
}
func roomViewController(_ roomViewController: RoomViewController, didRequestLiveLocationPresentationForBubbleData bubbleData: MXKRoomBubbleCellDataStoring) {
@@ -635,7 +635,7 @@ extension RoomCoordinator: RoomViewControllerDelegate {
}
// Tchap: Disable Live location sharing
-// showLiveLocationViewer(for: roomId)
+ showLiveLocationViewer(for: roomId)
}
func roomViewController(_ roomViewController: RoomViewController, locationShareActivityViewControllerFor event: MXEvent) -> UIActivityViewController? {
@@ -643,8 +643,7 @@ extension RoomCoordinator: RoomViewControllerDelegate {
return nil
}
- // Tchap: Location Sharing is disabled in Tchap
- return nil// LocationSharingCoordinator.shareLocationActivityController(CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
+ return LocationSharingCoordinator.shareLocationActivityController(CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
}
func roomViewController(_ roomViewController: RoomViewController, canEndPollWithEventIdentifier eventIdentifier: String) -> Bool {
@@ -672,8 +671,7 @@ extension RoomCoordinator: RoomViewControllerDelegate {
}
func roomViewControllerDidTapLiveLocationSharingBanner(_ roomViewController: RoomViewController) {
-
-// showLiveLocationViewer()
+ showLiveLocationViewer()
}
func roomViewControllerDidStopLiveLocationSharing(_ roomViewController: RoomViewController, beaconInfoEventId: String?) {
@@ -682,7 +680,7 @@ extension RoomCoordinator: RoomViewControllerDelegate {
return
}
-// self.stopLiveLocationSharing(forBeaconInfoEventId: beaconInfoEventId, inRoomWithId: roomId)
+ self.stopLiveLocationSharing(forBeaconInfoEventId: beaconInfoEventId, inRoomWithId: roomId)
}
func threadsCoordinator(for roomViewController: RoomViewController, threadId: String?) -> ThreadsCoordinatorBridgePresenter? {
diff --git a/Riot/Modules/Room/RoomViewController.h b/Riot/Modules/Room/RoomViewController.h
index 9029e33e72..a05b3a0156 100644
--- a/Riot/Modules/Room/RoomViewController.h
+++ b/Riot/Modules/Room/RoomViewController.h
@@ -32,9 +32,9 @@
@protocol RoomViewControllerDelegate;
@class RoomDisplayConfiguration;
// Tchap: Disable Threads
-// Tchap: Disable Live location sharing
//@class ThreadsCoordinatorBridgePresenter;
-//@class LiveLocationSharingBannerView;
+// Tchap: Disable Live location sharing
+@class LiveLocationSharingBannerView;
@class VoiceBroadcastService;
@class ComposerLinkActionBridgePresenter;
@@ -108,11 +108,11 @@ extern NSTimeInterval const kResizeComposerAnimationDuration;
@property (weak, nonatomic, nullable) IBOutlet UIStackView *topBannersStackView;
// Tchap: Disable Live location sharing
-// /// Indicate YES to show live location sharing banner
-//@property (nonatomic, readonly) BOOL shouldShowLiveLocationSharingBannerView;
-//
-// /// Displayed live location sharing banner if any
-//@property (nonatomic, weak) LiveLocationSharingBannerView *liveLocationSharingBannerView;
+ /// Indicate YES to show live location sharing banner
+@property (nonatomic, readonly) BOOL shouldShowLiveLocationSharingBannerView;
+
+ /// Displayed live location sharing banner if any
+@property (nonatomic, weak) LiveLocationSharingBannerView *liveLocationSharingBannerView;
// The customized room data source for Vector
@property (nonatomic, nullable) RoomDataSource *customizedRoomDataSource;
@@ -312,9 +312,9 @@ didRequestLocationPresentationForEvent:(MXEvent *)event
bubbleData:(id)bubbleData;
// Tchap: Disable Live location sharing
-/// Ask the coordinator to present the live location sharing viewer.
-//- (void)roomViewController:(RoomViewController *)roomViewController
-//didRequestLiveLocationPresentationForBubbleData:(id)bubbleData;
+// Ask the coordinator to present the live location sharing viewer.
+- (void)roomViewController:(RoomViewController *)roomViewController
+didRequestLiveLocationPresentationForBubbleData:(id)bubbleData;
- (nullable UIActivityViewController *)roomViewController:(RoomViewController *)roomViewController
locationShareActivityViewControllerForEvent:(MXEvent *)event;
@@ -349,7 +349,7 @@ didRequestEditForPollWithStartEvent:(MXEvent *)startEvent;
// Tchap: Disable Live location sharing
/// User tap live location sharing stop action
-//- (void)roomViewControllerDidStopLiveLocationSharing:(RoomViewController *)roomViewController beaconInfoEventId:(nullable NSString*)beaconInfoEventId;
+- (void)roomViewControllerDidStopLiveLocationSharing:(RoomViewController *)roomViewController beaconInfoEventId:(nullable NSString*)beaconInfoEventId;
/// User tap live location sharing banner
- (void)roomViewControllerDidTapLiveLocationSharingBanner:(RoomViewController *)roomViewController;
diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m
index b0024715e8..b93c1e4ac9 100644
--- a/Riot/Modules/Room/RoomViewController.m
+++ b/Riot/Modules/Room/RoomViewController.m
@@ -1657,10 +1657,10 @@ - (void)setMissedDiscussionsBadgeHidden:(BOOL)missedDiscussionsBadgeHidden{
}
// Tchap: Disable Live location sharing
-//- (BOOL)shouldShowLiveLocationSharingBannerView
-//{
-// return self.customizedRoomDataSource.isCurrentUserSharingActiveLocation;
-//}
+- (BOOL)shouldShowLiveLocationSharingBannerView
+{
+ return self.customizedRoomDataSource.isCurrentUserSharingActiveLocation;
+}
- (void)setForceHideInputToolBar:(BOOL)forceHideInputToolBar
{
@@ -2425,7 +2425,10 @@ - (void)setupActions {
[self.delegate roomViewControllerDidRequestPollCreationFormPresentation:self];
}]];
}
- if (BuildSettings.locationSharingEnabled && !self.isNewDirectChat)
+ // Tchap: allow location sharing by feature flag
+// if (BuildSettings.locationSharingEnabled && !self.isNewDirectChat)
+ MXKAccount *account = MXKAccountManager.sharedManager.activeAccounts.firstObject;
+ if ([account isFeatureActivated:BuildSettings.tchapFeatureGeolocationSharing] && BuildSettings.locationSharingEnabled && !self.isNewDirectChat)
{
[actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionLocation.image andAction:^{
MXStrongifyAndReturnIfNil(self);
@@ -3330,39 +3333,39 @@ - (RoomTimelineCellIdentifier)cellIdentifierForCellData:(MXKCellData*)cellData a
}
}
}
-// else if (bubbleData.tag == RoomBubbleCellDataTagLocation || bubbleData.tag == RoomBubbleCellDataTagLiveLocation)
-// {
-// if (bubbleData.isIncoming)
-// {
-// if (bubbleData.isPaginationFirstBubble)
-// {
-// cellIdentifier = RoomTimelineCellIdentifierIncomingLocationWithPaginationTitle;
-// }
-// else if (bubbleData.shouldHideSenderInformation)
-// {
-// cellIdentifier = RoomTimelineCellIdentifierIncomingLocationWithoutSenderInfo;
-// }
-// else
-// {
-// cellIdentifier = RoomTimelineCellIdentifierIncomingLocation;
-// }
-// }
-// else
-// {
-// if (bubbleData.isPaginationFirstBubble)
-// {
-// cellIdentifier = RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle;
-// }
-// else if (bubbleData.shouldHideSenderInformation)
-// {
-// cellIdentifier = RoomTimelineCellIdentifierOutgoingLocationWithoutSenderInfo;
-// }
-// else
-// {
-// cellIdentifier = RoomTimelineCellIdentifierOutgoingLocation;
-// }
-// }
-// }
+ else if (bubbleData.tag == RoomBubbleCellDataTagLocation || bubbleData.tag == RoomBubbleCellDataTagLiveLocation)
+ {
+ if (bubbleData.isIncoming)
+ {
+ if (bubbleData.isPaginationFirstBubble)
+ {
+ cellIdentifier = RoomTimelineCellIdentifierIncomingLocationWithPaginationTitle;
+ }
+ else if (bubbleData.shouldHideSenderInformation)
+ {
+ cellIdentifier = RoomTimelineCellIdentifierIncomingLocationWithoutSenderInfo;
+ }
+ else
+ {
+ cellIdentifier = RoomTimelineCellIdentifierIncomingLocation;
+ }
+ }
+ else
+ {
+ if (bubbleData.isPaginationFirstBubble)
+ {
+ cellIdentifier = RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle;
+ }
+ else if (bubbleData.shouldHideSenderInformation)
+ {
+ cellIdentifier = RoomTimelineCellIdentifierOutgoingLocationWithoutSenderInfo;
+ }
+ else
+ {
+ cellIdentifier = RoomTimelineCellIdentifierOutgoingLocation;
+ }
+ }
+ }
// else if (bubbleData.tag == RoomBubbleCellDataTagVoiceBroadcastPlayback)
// {
// if (bubbleData.isIncoming)
@@ -3630,26 +3633,26 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac
[self mention:roomMember];
}
}
-// else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellStopShareButtonPressed])
-// {
-// NSString *beaconInfoEventId;
-//
-// if ([bubbleData isKindOfClass:[RoomBubbleCellData class]])
-// {
-// RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleData;
-// beaconInfoEventId = roomBubbleCellData.beaconInfoSummary.id;
-// }
-//
-// [self.delegate roomViewControllerDidStopLiveLocationSharing:self beaconInfoEventId:beaconInfoEventId];
-// }
-// else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellRetryShareButtonPressed])
-// {
-// MXEvent *selectedEvent = userInfo[kMXKRoomBubbleCellEventKey];
-// if (selectedEvent)
-// {
-// // TODO: - Implement retry live location action
-// }
-// }
+ else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellStopShareButtonPressed])
+ {
+ NSString *beaconInfoEventId;
+
+ if ([bubbleData isKindOfClass:[RoomBubbleCellData class]])
+ {
+ RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleData;
+ beaconInfoEventId = roomBubbleCellData.beaconInfoSummary.id;
+ }
+
+ [self.delegate roomViewControllerDidStopLiveLocationSharing:self beaconInfoEventId:beaconInfoEventId];
+ }
+ else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellRetryShareButtonPressed])
+ {
+ MXEvent *selectedEvent = userInfo[kMXKRoomBubbleCellEventKey];
+ if (selectedEvent)
+ {
+ // TODO: - Implement retry live location action
+ }
+ }
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnMessageTextView] || [actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnContentView])
{
// Retrieve the tapped event
@@ -3660,10 +3663,10 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac
{
[self cancelEventSelection];
}
-// else if (bubbleData.tag == RoomBubbleCellDataTagLiveLocation)
-// {
-// [self.delegate roomViewController:self didRequestLiveLocationPresentationForBubbleData:bubbleData];
-// }
+ else if (bubbleData.tag == RoomBubbleCellDataTagLiveLocation)
+ {
+ [self.delegate roomViewController:self didRequestLiveLocationPresentationForBubbleData:bubbleData];
+ }
else if (tappedEvent)
{
if (tappedEvent.eventType == MXEventTypeRoomCreate)
@@ -7767,6 +7770,15 @@ - (void)documentPickerPresenter:(MXKDocumentPickerPresenter *)presenter didPickD
{
self.documentPickerPresenter = nil;
+ // Tchap: check maxUploadSize accepted by the home server before trying to upload.
+ NSUInteger maxUploadFileSize = self.roomDataSource.mxSession.maxUploadSize;
+ NSDictionary *fileAttributes = [NSFileManager.defaultManager attributesOfItemAtPath:url.path error:nil];
+ if (fileAttributes && fileAttributes.fileSize > maxUploadFileSize) {
+ [self showAlertWithTitle:TchapL10n.roomSendFileTooBigTitle
+ message:[TchapL10n roomSendFileTooBigMessage:maxUploadFileSize/(1024*1024) :fileAttributes.fileSize/(1024*1024)]];
+ return;
+ }
+
MXKUTI *fileUTI = [[MXKUTI alloc] initWithLocalFileURL:url];
NSString *mimeType = fileUTI.mimeType;
diff --git a/Riot/Modules/Room/TimelineCells/Call/Direct/RoomDirectCallStatusCell.swift b/Riot/Modules/Room/TimelineCells/Call/Direct/RoomDirectCallStatusCell.swift
index 84e3973e46..5dc216d027 100644
--- a/Riot/Modules/Room/TimelineCells/Call/Direct/RoomDirectCallStatusCell.swift
+++ b/Riot/Modules/Room/TimelineCells/Call/Direct/RoomDirectCallStatusCell.swift
@@ -111,6 +111,11 @@ class RoomDirectCallStatusCell: RoomCallBaseCell {
}
}
+ // Tchap: report VoIP problem button icon ô
+ private var reportVoIPProblemButtonIcon: UIImage {
+ return UIImage(systemName: "exclamationmark.circle.fill")!
+ }
+
private var actionUserInfo: [AnyHashable: Any]? {
if let event = callInviteEvent {
return [kMXKRoomBubbleCellEventKey: event]
@@ -179,7 +184,7 @@ class RoomDirectCallStatusCell: RoomCallBaseCell {
view.firstButton.style = .positive
view.firstButton.setTitle(TchapL10n.eventFormatterReportIncident, for: .normal)
- view.firstButton.setImage(callButtonIcon, for: .normal)
+ view.firstButton.setImage(reportVoIPProblemButtonIcon, for: .normal)
view.firstButton.removeTarget(nil, action: nil, for: .touchUpInside)
view.firstButton.addTarget(self, action: #selector(reportIncidentAction(_:)), for: .touchUpInside)
diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m
index 2dc24aa911..1e2acac960 100644
--- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m
+++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m
@@ -110,14 +110,14 @@ - (void)registerPollCellsForTableView:(UITableView *)tableView
- (void)registerLocationCellsForTableView:(UITableView*)tableView
{
-// // Incoming
-// [tableView registerClass:LocationIncomingBubbleCell.class forCellReuseIdentifier:LocationIncomingBubbleCell.defaultReuseIdentifier];
-// [tableView registerClass:LocationIncomingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:LocationIncomingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
-// [tableView registerClass:LocationIncomingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:LocationIncomingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
-//
-// // Outgoing
-// [tableView registerClass:LocationOutgoingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:LocationOutgoingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
-// [tableView registerClass:LocationOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:LocationOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
+ // Incoming
+ [tableView registerClass:LocationIncomingBubbleCell.class forCellReuseIdentifier:LocationIncomingBubbleCell.defaultReuseIdentifier];
+ [tableView registerClass:LocationIncomingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:LocationIncomingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
+ [tableView registerClass:LocationIncomingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:LocationIncomingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
+
+ // Outgoing
+ [tableView registerClass:LocationOutgoingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:LocationOutgoingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
+ [tableView registerClass:LocationOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:LocationOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
}
- (void)registerFileWithoutThumbnailCellsForTableView:(UITableView*)tableView
@@ -299,7 +299,7 @@ - (void)registerVoiceBroadcastRecorderCellsForTableView:(UITableView*)tableView
- (NSDictionary*)locationCellsMapping
{
- return @{/*
+ return @{
// Incoming
@(RoomTimelineCellIdentifierIncomingLocation) : LocationIncomingBubbleCell.class,
@(RoomTimelineCellIdentifierIncomingLocationWithoutSenderInfo) : LocationIncomingWithoutSenderInfoBubbleCell.class,
@@ -307,7 +307,7 @@ - (void)registerVoiceBroadcastRecorderCellsForTableView:(UITableView*)tableView
// Outgoing
@(RoomTimelineCellIdentifierOutgoingLocation) : LocationOutgoingWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingLocationWithoutSenderInfo) : LocationOutgoingWithoutSenderInfoBubbleCell.class,
- @(RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle) : LocationOutgoingWithPaginationTitleBubbleCell.class*/
+ @(RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle) : LocationOutgoingWithPaginationTitleBubbleCell.class
};
}
diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m
index e178d77235..f5b493a32a 100644
--- a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m
+++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m
@@ -268,9 +268,9 @@ - (void)registerPollCellsForTableView:(UITableView*)tableView
- (void)registerLocationCellsForTableView:(UITableView*)tableView
{
-// [tableView registerClass:LocationCell.class forCellReuseIdentifier:LocationCell.defaultReuseIdentifier];
-// [tableView registerClass:LocationWithoutSenderInfoCell.class forCellReuseIdentifier:LocationWithoutSenderInfoCell.defaultReuseIdentifier];
-// [tableView registerClass:LocationWithPaginationTitleCell.class forCellReuseIdentifier:LocationWithPaginationTitleCell.defaultReuseIdentifier];
+ [tableView registerClass:LocationPlainCell.class forCellReuseIdentifier:LocationPlainCell.defaultReuseIdentifier];
+ [tableView registerClass:LocationWithoutSenderInfoPlainCell.class forCellReuseIdentifier:LocationWithoutSenderInfoPlainCell.defaultReuseIdentifier];
+ [tableView registerClass:LocationWithPaginationTitlePlainCell.class forCellReuseIdentifier:LocationWithPaginationTitlePlainCell.defaultReuseIdentifier];
}
- (void)registerAntivirusCellsForTableView:(UITableView*)tableView
@@ -580,13 +580,13 @@ - (void)registerVoiceBroadcastRecorderCellsForTableView:(UITableView*)tableView
{
return @{
// Incoming
-// @(RoomTimelineCellIdentifierIncomingLocation) : LocationPlainCell.class,
-// @(RoomTimelineCellIdentifierIncomingLocationWithoutSenderInfo) : LocationWithoutSenderInfoPlainCell.class,
-// @(RoomTimelineCellIdentifierIncomingLocationWithPaginationTitle) : LocationWithPaginationTitlePlainCell.class,
+ @(RoomTimelineCellIdentifierIncomingLocation) : LocationPlainCell.class,
+ @(RoomTimelineCellIdentifierIncomingLocationWithoutSenderInfo) : LocationWithoutSenderInfoPlainCell.class,
+ @(RoomTimelineCellIdentifierIncomingLocationWithPaginationTitle) : LocationWithPaginationTitlePlainCell.class,
// Outgoing
-// @(RoomTimelineCellIdentifierOutgoingLocation) : LocationPlainCell.class,
-// @(RoomTimelineCellIdentifierOutgoingLocationWithoutSenderInfo) : LocationWithoutSenderInfoPlainCell.class,
-// @(RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle) : LocationWithPaginationTitlePlainCell.class
+ @(RoomTimelineCellIdentifierOutgoingLocation) : LocationPlainCell.class,
+ @(RoomTimelineCellIdentifierOutgoingLocationWithoutSenderInfo) : LocationWithoutSenderInfoPlainCell.class,
+ @(RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle) : LocationWithPaginationTitlePlainCell.class
};
}
diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m
index 06a4e1ee1a..1bdbdf03ae 100644
--- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m
+++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m
@@ -234,7 +234,9 @@ - (void)updateToolbarButtonLabelWithPreviousMode:(RoomInputToolbarViewSendMode)p
self.textView.maxHeight -= kContextBarHeight;
break;
case RoomInputToolbarViewSendModeEdit:
- buttonImage = AssetImages.saveIcon.image;
+ // Tchap: use Tchap icon to save edited message.
+ // buttonImage = AssetImages.saveIcon.image;
+ buttonImage = AssetImages_tchap.sendIconTchap.image ;
self.inputContextImageView.image = AssetImages.inputEditIcon.image;
self.inputContextLabel.text = [VectorL10n roomMessageEditing];
diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m
index e76ab67acb..23151dbe15 100644
--- a/Riot/Modules/Settings/SettingsViewController.m
+++ b/Riot/Modules/Settings/SettingsViewController.m
@@ -190,8 +190,7 @@ typedef NS_ENUM(NSUInteger, LABS_ENABLE)
LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX = 0,
LABS_ENABLE_THREADS_INDEX,
LABS_ENABLE_AUTO_REPORT_DECRYPTION_ERRORS,
- // Tchap: Location sharing is disabled in Tchap
-// LABS_ENABLE_LIVE_LOCATION_SHARING,
+ LABS_ENABLE_LIVE_LOCATION_SHARING,
LABS_ENABLE_NEW_SESSION_MANAGER,
LABS_ENABLE_NEW_CLIENT_INFO_FEATURE,
LABS_ENABLE_WYSIWYG_COMPOSER,
@@ -681,8 +680,7 @@ - (void)updateSections
[sectionLabs addRowWithTag:LABS_ENABLE_AUTO_REPORT_DECRYPTION_ERRORS];
if (BuildSettings.locationSharingEnabled)
{
- // Tchap: Location sharing is disabled in Tchap
-// [sectionLabs addRowWithTag:LABS_ENABLE_LIVE_LOCATION_SHARING];
+ [sectionLabs addRowWithTag:LABS_ENABLE_LIVE_LOCATION_SHARING];
}
[sectionLabs addRowWithTag:LABS_ENABLE_NEW_SESSION_MANAGER];
[sectionLabs addRowWithTag:LABS_ENABLE_NEW_CLIENT_INFO_FEATURE];
@@ -2107,8 +2105,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
// Tchap: Add Hide User from directory
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
- NSString *title = NSLocalizedStringFromTable(@"settings_hide_from_users_directory_title", @"Tchap", nil);
- NSString *summary = NSLocalizedStringFromTable(@"settings_hide_from_users_directory_summary", @"Tchap", nil);
+ NSString *title = TchapL10n.settingsHideFromUsersDirectoryTitle;
+ NSString *summary = TchapL10n.settingsHideFromUsersDirectorySummary;
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString: title
attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
NSFontAttributeName: [UIFont systemFontOfSize:17.0]}];
@@ -2171,7 +2169,18 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
- labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsEnablePushNotif];
+ // Tchap: adapt "Enable notifications" label and text.
+// labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsEnablePushNotif];
+
+ NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString: VectorL10n.settingsEnablePushNotif
+ attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
+ NSFontAttributeName: [UIFont systemFontOfSize:17.0]}];
+ [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}]];
+ [attributedText appendAttributedString:[[NSMutableAttributedString alloc] initWithString: TchapL10n.settingsEnablePushNotifText
+ attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textSecondaryColor,
+ NSFontAttributeName: [UIFont systemFontOfSize:14.0]}]];
+
+ labelAndSwitchCell.mxkLabel.attributedText = attributedText;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePushNotifications:) forControlEvents:UIControlEventTouchUpInside];
@@ -2232,7 +2241,34 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
- labelAndSwitchCell.mxkLabel.text = TchapL10n.settingsNotificationEmail;
+
+
+ // Tchap: add explanation to "enable notiofications by email"
+// labelAndSwitchCell.mxkLabel.text = TchapL10n.settingsNotificationEmail;
+
+ NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString: TchapL10n.settingsNotificationEmail
+ attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textPrimaryColor,
+ NSFontAttributeName: [UIFont systemFontOfSize:17.0]}];
+ [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}]];
+ [attributedText appendAttributedString:[[NSMutableAttributedString alloc] initWithString: TchapL10n.settingsEnableEmailNotifText
+ attributes:@{NSForegroundColorAttributeName : ThemeService.shared.theme.textSecondaryColor,
+ NSFontAttributeName: [UIFont systemFontOfSize:14.0]}]];
+
+ [attributedText appendString:@" "];
+
+ [attributedText appendAttributedString:
+ [[NSAttributedString alloc] initWithString:TchapL10n.settingsEnableEmailNotifLink
+ attributes:@{
+ NSForegroundColorAttributeName: ThemeService.shared.theme.textSecondaryColor,
+ NSFontAttributeName: [UIFont systemFontOfSize:14.0],
+ NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleSingle]
+ }]];
+
+ labelAndSwitchCell.mxkLabel.attributedText = attributedText;
+
+ [labelAndSwitchCell.mxkLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(displayEmailNotificationFaq)]];
+ labelAndSwitchCell.mxkLabel.userInteractionEnabled = YES;
+
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleNotificationsByEmail:) forControlEvents:UIControlEventTouchUpInside];
@@ -2691,11 +2727,10 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
{
cell = [self buildAutoReportDecryptionErrorsCellForTableView:tableView atIndexPath:indexPath];
}
- // Tchap: Location sharing is disabled in Tchap
-// else if (row == LABS_ENABLE_LIVE_LOCATION_SHARING)
-// {
-// cell = [self buildLiveLocationSharingCellForTableView:tableView atIndexPath:indexPath];
-// }
+ else if (row == LABS_ENABLE_LIVE_LOCATION_SHARING)
+ {
+ cell = [self buildLiveLocationSharingCellForTableView:tableView atIndexPath:indexPath];
+ }
else if (row == LABS_ENABLE_NEW_SESSION_MANAGER)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
@@ -5119,4 +5154,12 @@ - (void)showUserSessionsFlow
// MXLogWarning(@"Unexpected callback after OIDC account management.")
//}
+// Tchap: display email notification faq
+- (void)displayEmailNotificationFaq {
+ NSString *targetUrlString = @"https://aide.tchap.beta.gouv.fr/fr/article/notification-par-email-draft-6k7k89/";
+
+ WebSheetViewController *webCtrl = [[WebSheetViewController alloc] initWithTargetUrl:[NSURL URLWithString:targetUrlString]];
+ [self presentViewController:webCtrl animated:YES completion:nil];
+}
+
@end
diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift
index a5cde02ff3..24125ae49c 100644
--- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift
+++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift
@@ -428,8 +428,7 @@ extension ExploreRoomCoordinator: RoomViewControllerDelegate {
guard let location = event.location else {
return nil
}
- // Tchap: No location sharing available in Tchap
- return nil// LocationSharingCoordinator.shareLocationActivityController(CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
+ return LocationSharingCoordinator.shareLocationActivityController(CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
}
func roomViewController(_ roomViewController: RoomViewController, canEditPollWithEventIdentifier eventIdentifier: String) -> Bool {
diff --git a/Riot/SupportingFiles/PrivacyInfo.xcprivacy b/Riot/SupportingFiles/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..500ae9affe
--- /dev/null
+++ b/Riot/SupportingFiles/PrivacyInfo.xcprivacy
@@ -0,0 +1,41 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ 1C8F.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ 7D9E.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 3D61.1
+
+
+
+
+
diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m
index 27b6a99065..d456343ea1 100644
--- a/Riot/Utils/EventFormatter.m
+++ b/Riot/Utils/EventFormatter.m
@@ -23,7 +23,6 @@
#import "WidgetManager.h"
#import "MXDecryptionResult.h"
-#import "DecryptionFailureTracker.h"
#import
@@ -370,8 +369,10 @@ - (NSAttributedString *)unsafeAttributedStringFromEvent:(MXEvent *)event
// }]];
NSMutableAttributedString *attributedStringWithRerequestMessage = [attributedString mutableCopy];
+ // Tchap: Reset initial String to optimize error message for user
+ [attributedStringWithRerequestMessage.mutableString setString: [NSString stringWithFormat:@"%@\n", VectorL10n.noticeCryptoErrorUnknownInboundSessionId]];
- [attributedStringWithRerequestMessage appendString:[NSString stringWithFormat:@" %@\n", VectorL10n.noticeCryptoErrorUnknownInboundSessionId]];
+// [attributedStringWithRerequestMessage appendString:[NSString stringWithFormat:@" %@\n", VectorL10n.noticeCryptoErrorUnknownInboundSessionId]];
NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", EventFormatterFaqLinkAction,
EventFormatterLinkActionSeparator,
@"https://aide.tchap.beta.gouv.fr/fr/article/dechiffrement-impossible-de-mes-messages-comment-y-remedier-iphone-xotgv1"];
@@ -385,6 +386,11 @@ - (NSAttributedString *)unsafeAttributedStringFromEvent:(MXEvent *)event
NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleSingle]
}]];
+ [attributedStringWithRerequestMessage addAttributes:@{
+ NSForegroundColorAttributeName: [UIColor colorWithRed:106.0/255.0 green:106.0/255.0 blue:106.0/255.0 alpha:1.0],
+ NSFontAttributeName: self.encryptedMessagesTextFont,
+ } range:NSMakeRange(0, attributedStringWithRerequestMessage.length)];
+
attributedString = attributedStringWithRerequestMessage;
}
}
diff --git a/RiotNSE/BuildSettings.swift b/RiotNSE/BuildSettings.swift
index 15bd01100e..ce637a7d40 100644
--- a/RiotNSE/BuildSettings.swift
+++ b/RiotNSE/BuildSettings.swift
@@ -267,17 +267,21 @@ final class BuildSettings: NSObject {
static let tchapFeatureNotificationByEmail = "tchapFeatureNotificationByEmail"
static let tchapFeatureVoiceOverIP = "tchapFeatureVoiceOverIP"
static let tchapFeatureVideoOverIP = "tchapFeatureVideoOverIP"
+ static let tchapFeatureGeolocationSharing = "tchapFeatureGeolocationSharing" // linked to `locationSharingEnabled` property (see above)
static var tchapFeaturesAllowedHomeServersForFeature: [String: [String]] = [
tchapFeatureNotificationByEmail: [
- "agent.dinum.tchap.gouv.fr"
+ tchapFeatureAnyHomeServer
],
tchapFeatureVoiceOverIP: [
"agent.dinum.tchap.gouv.fr"
- ]
+ ],
// No activation of video calls actually in Tchap Production.
// tchapFeatureVideoOverIP: [
// "agent.dinum.tchap.gouv.fr"
// ],
+ tchapFeatureGeolocationSharing: [
+ tchapFeatureAnyHomeServer
+ ]
]
// MARK: - Side Menu
@@ -452,9 +456,14 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
/// Overwritten by the home server's .well-known configuration (if any exists)
- static let defaultTileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=")!
-
- static let locationSharingEnabled = false // Currently disabled in Tchap.
+ // Tchap: handle different map providers.
+ private enum TchapMapProvider: String {
+ case geoDataGouv = "https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
+ case ign = "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json"
+ }
+ static let defaultTileServerMapStyleURL = URL(string: TchapMapProvider.geoDataGouv.rawValue)!
+
+ static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
diff --git a/RiotNSE/Common.xcconfig b/RiotNSE/Common.xcconfig
index 5fb618d128..19b42ed0c8 100644
--- a/RiotNSE/Common.xcconfig
+++ b/RiotNSE/Common.xcconfig
@@ -19,7 +19,10 @@
#include "Tchap/SupportingFiles/App-Common.xcconfig"
-PRODUCT_NAME = RiotNSE
+
+// Tchap: Customize TchapNSE user-agent for back-end logs.
+// PRODUCT_NAME = RiotNSE
+PRODUCT_NAME = TchapNSE
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).nse
INFOPLIST_FILE = RiotNSE/Info.plist
@@ -27,5 +30,8 @@ INFOPLIST_FILE = RiotNSE/Info.plist
CODE_SIGN_ENTITLEMENTS = RiotNSE/RiotNSE.entitlements
SKIP_INSTALL = YES
-SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotNSE-Bridging-Header.h
+
+// Tchap: Customize TchapNSE user-agent for back-end logs.
+// SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotNSE-Bridging-Header.h
+SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h
diff --git a/RiotNSE/SupportingFiles/PrivacyInfo.xcprivacy b/RiotNSE/SupportingFiles/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..500ae9affe
--- /dev/null
+++ b/RiotNSE/SupportingFiles/PrivacyInfo.xcprivacy
@@ -0,0 +1,41 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ 1C8F.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ 7D9E.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 3D61.1
+
+
+
+
+
diff --git a/RiotShareExtension/BuildSettings.swift b/RiotShareExtension/BuildSettings.swift
index 15bd01100e..ce637a7d40 100644
--- a/RiotShareExtension/BuildSettings.swift
+++ b/RiotShareExtension/BuildSettings.swift
@@ -267,17 +267,21 @@ final class BuildSettings: NSObject {
static let tchapFeatureNotificationByEmail = "tchapFeatureNotificationByEmail"
static let tchapFeatureVoiceOverIP = "tchapFeatureVoiceOverIP"
static let tchapFeatureVideoOverIP = "tchapFeatureVideoOverIP"
+ static let tchapFeatureGeolocationSharing = "tchapFeatureGeolocationSharing" // linked to `locationSharingEnabled` property (see above)
static var tchapFeaturesAllowedHomeServersForFeature: [String: [String]] = [
tchapFeatureNotificationByEmail: [
- "agent.dinum.tchap.gouv.fr"
+ tchapFeatureAnyHomeServer
],
tchapFeatureVoiceOverIP: [
"agent.dinum.tchap.gouv.fr"
- ]
+ ],
// No activation of video calls actually in Tchap Production.
// tchapFeatureVideoOverIP: [
// "agent.dinum.tchap.gouv.fr"
// ],
+ tchapFeatureGeolocationSharing: [
+ tchapFeatureAnyHomeServer
+ ]
]
// MARK: - Side Menu
@@ -452,9 +456,14 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
/// Overwritten by the home server's .well-known configuration (if any exists)
- static let defaultTileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=")!
-
- static let locationSharingEnabled = false // Currently disabled in Tchap.
+ // Tchap: handle different map providers.
+ private enum TchapMapProvider: String {
+ case geoDataGouv = "https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
+ case ign = "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json"
+ }
+ static let defaultTileServerMapStyleURL = URL(string: TchapMapProvider.geoDataGouv.rawValue)!
+
+ static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
diff --git a/RiotShareExtension/SupportingFiles/PrivacyInfo.xcprivacy b/RiotShareExtension/SupportingFiles/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..c3b45f6413
--- /dev/null
+++ b/RiotShareExtension/SupportingFiles/PrivacyInfo.xcprivacy
@@ -0,0 +1,41 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ 1C8F.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ 7D9E.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 3D61.1
+
+
+
+
+
\ No newline at end of file
diff --git a/RiotSwiftUI/Info.plist b/RiotSwiftUI/Info.plist
index a74d218642..df3ec702a8 100644
--- a/RiotSwiftUI/Info.plist
+++ b/RiotSwiftUI/Info.plist
@@ -23,8 +23,8 @@
CFBundleDisplayName
RiotSwiftUI
NSLocationWhenInUseUsageDescription
- When you share your location to people, Element needs access to show them a map.
+ When you share your location to people, Tchap needs access to show them a map.
NSLocationAlwaysAndWhenInUseUsageDescription
- When you share your location to people, Element needs access to show them a map.
+ When you share your location to people, Tchap needs access to show them a map.
diff --git a/RiotSwiftUI/Modules/Common/Test/UI/XCUIElement.swift b/RiotSwiftUI/Modules/Common/Test/UI/XCUIElement.swift
new file mode 100644
index 0000000000..db41a603ef
--- /dev/null
+++ b/RiotSwiftUI/Modules/Common/Test/UI/XCUIElement.swift
@@ -0,0 +1,28 @@
+//
+// Copyright 2024 New Vector Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import XCTest
+
+extension XCUIElement {
+ func forceTap() {
+ if isHittable {
+ tap()
+ } else {
+ let coordinate: XCUICoordinate = coordinate(withNormalizedOffset: .init(dx: 0.5, dy: 0.5))
+ coordinate.tap()
+ }
+ }
+}
diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift
index f2a60ce066..5850980920 100644
--- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift
+++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift
@@ -137,7 +137,7 @@ struct Composer: View {
placeholder: viewModel.viewState.placeholder ?? "",
viewModel: wysiwygViewModel,
itemProviderHelper: nil,
- keyCommandHandler: handleKeyCommand,
+ keyCommands: keyCommands,
pasteHandler: nil
)
.clipped()
@@ -228,15 +228,13 @@ struct Composer: View {
}
}
- func handleKeyCommand(_ keyCommand: WysiwygKeyCommand) -> Bool {
- switch keyCommand {
- case .enter:
- sendMessageAction(wysiwygViewModel.content)
- wysiwygViewModel.clearContent()
- return true
- case .shiftEnter:
- return false
- }
+ var keyCommands: [WysiwygKeyCommand] {
+ [
+ .enter {
+ sendMessageAction(wysiwygViewModel.content)
+ wysiwygViewModel.clearContent()
+ }
+ ]
}
/// Computes the total height of the composer (excluding the RTE formatting bar).
diff --git a/RiotSwiftUI/Modules/Settings/Notifications/Test/Unit/NotificationSettingsViewModelTests.swift b/RiotSwiftUI/Modules/Settings/Notifications/Test/Unit/NotificationSettingsViewModelTests.swift
index 95b5e08fad..b241dcfcee 100644
--- a/RiotSwiftUI/Modules/Settings/Notifications/Test/Unit/NotificationSettingsViewModelTests.swift
+++ b/RiotSwiftUI/Modules/Settings/Notifications/Test/Unit/NotificationSettingsViewModelTests.swift
@@ -41,37 +41,6 @@ final class NotificationSettingsViewModelTests: XCTestCase {
XCTAssertEqual(viewModel.viewState.selectionState[.encrypted], false)
}
- func testUpdateOneToOneRuleAlsoUpdatesPollRules() async {
- setupWithPollRules()
-
- await viewModel.update(ruleID: .oneToOneRoom, isChecked: false)
-
- XCTAssertEqual(viewModel.viewState.selectionState.count, 8)
- XCTAssertEqual(viewModel.viewState.selectionState[.oneToOneRoom], false)
- XCTAssertEqual(viewModel.viewState.selectionState[.oneToOnePollStart], false)
- XCTAssertEqual(viewModel.viewState.selectionState[.oneToOnePollEnd], false)
-
- // unrelated poll rules stay the same
- XCTAssertEqual(viewModel.viewState.selectionState[.allOtherMessages], true)
- XCTAssertEqual(viewModel.viewState.selectionState[.pollStart], true)
- XCTAssertEqual(viewModel.viewState.selectionState[.pollEnd], true)
- }
-
- func testUpdateMessageRuleAlsoUpdatesPollRules() async {
- setupWithPollRules()
-
- await viewModel.update(ruleID: .allOtherMessages, isChecked: false)
- XCTAssertEqual(viewModel.viewState.selectionState.count, 8)
- XCTAssertEqual(viewModel.viewState.selectionState[.allOtherMessages], false)
- XCTAssertEqual(viewModel.viewState.selectionState[.pollStart], false)
- XCTAssertEqual(viewModel.viewState.selectionState[.pollEnd], false)
-
- // unrelated poll rules stay the same
- XCTAssertEqual(viewModel.viewState.selectionState[.oneToOneRoom], true)
- XCTAssertEqual(viewModel.viewState.selectionState[.oneToOnePollStart], true)
- XCTAssertEqual(viewModel.viewState.selectionState[.oneToOnePollEnd], true)
- }
-
func testMismatchingRulesAreHandled() async {
setupWithPollRules()
diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/UI/UserOtherSessionsUITests.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/UI/UserOtherSessionsUITests.swift
index 71a6516592..3fcf2a8ccd 100644
--- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/UI/UserOtherSessionsUITests.swift
+++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/UI/UserOtherSessionsUITests.swift
@@ -56,7 +56,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenOtherSessionsMoreMenuButtonSelected_moreMenuIsCorrect() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.all.title)
- app.buttons["More"].tap()
+ app.buttons["More"].forceTap()
XCTAssertTrue(app.buttons["Select sessions"].exists)
XCTAssertTrue(app.buttons["Sign out of 6 sessions"].exists)
}
@@ -64,7 +64,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenOtherSessionsSelectSessionsSelected_navBarContainsCorrectButtons() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.all.title)
- app.buttons["More"].tap()
+ app.buttons["More"].forceTap()
app.buttons["Select sessions"].tap()
let signOutButton = app.buttons["Sign out"]
XCTAssertTrue(signOutButton.exists)
@@ -76,7 +76,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenOtherSessionsSelectAllSelected_navBarContainsCorrectButtons() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.all.title)
- app.buttons["More"].tap()
+ app.buttons["More"].forceTap()
app.buttons["Select sessions"].tap()
app.buttons["Select All"].tap()
XCTAssertTrue(app.buttons["Deselect All"].exists)
@@ -85,7 +85,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenAllOtherSessionsAreSelected_navBarContainsCorrectButtons() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.all.title)
- app.buttons["More"].tap()
+ app.buttons["More"].forceTap()
app.buttons["Select sessions"].tap()
for i in 0...MockUserOtherSessionsScreenState.all.allSessions().count - 1 {
app.buttons["UserSessionListItem_\(i)"].tap()
@@ -95,7 +95,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenChangingSessionSelection_signOutButtonChangesItState() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.all.title)
- app.buttons["More"].tap()
+ app.buttons["More"].forceTap()
app.buttons["Select sessions"].tap()
let signOutButton = app.buttons["Sign out"]
XCTAssertTrue(signOutButton.exists)
diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift
index ba844904ec..39970240d9 100644
--- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift
+++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift
@@ -80,35 +80,33 @@ struct UserOtherSessionsToolbar: ToolbarContent {
}
private func optionsMenu() -> some View {
- Button { } label: {
- Menu {
- if showDeviceLogout { // As you can only sign out the selected sessions, we don't allow selection when you're unable to sign out devices.
- Button {
- isEditModeEnabled = true
- } label: {
- Label(VectorL10n.userOtherSessionMenuSelectSessions, systemImage: "checkmark.circle")
- }
- .disabled(sessionCount == 0)
- }
-
+ Menu {
+ if showDeviceLogout { // As you can only sign out the selected sessions, we don't allow selection when you're unable to sign out devices.
Button {
- isShowLocationEnabled.toggle()
+ isEditModeEnabled = true
} label: {
- Label(showLocationInfo: isShowLocationEnabled)
- }
-
- if sessionCount > 0, showDeviceLogout {
- DestructiveButton {
- onSignOut()
- } label: {
- Label(VectorL10n.userOtherSessionMenuSignOutSessions(String(sessionCount)), systemImage: "rectangle.portrait.and.arrow.forward.fill")
- }
+ Label(VectorL10n.userOtherSessionMenuSelectSessions, systemImage: "checkmark.circle")
}
+ .disabled(sessionCount == 0)
+ }
+
+ Button {
+ isShowLocationEnabled.toggle()
} label: {
- Image(systemName: "ellipsis")
- .padding(.horizontal, 4)
- .padding(.vertical, 12)
+ Label(showLocationInfo: isShowLocationEnabled)
+ }
+
+ if sessionCount > 0, showDeviceLogout {
+ DestructiveButton {
+ onSignOut()
+ } label: {
+ Label(VectorL10n.userOtherSessionMenuSignOutSessions(String(sessionCount)), systemImage: "rectangle.portrait.and.arrow.forward.fill")
+ }
}
+ } label: {
+ Image(systemName: "ellipsis")
+ .padding(.horizontal, 4)
+ .padding(.vertical, 12)
}
.accessibilityIdentifier("More")
}
diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift
index 643c28cbe9..93938057ca 100644
--- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift
+++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/UI/UserSessionOverviewUITests.swift
@@ -67,7 +67,7 @@ class UserSessionOverviewUITests: MockScreenTestCase {
let navTitle = VectorL10n.userSessionOverviewSessionTitle
let barButton = app.navigationBars[navTitle].buttons["Menu"]
XCTAssertTrue(barButton.exists)
- barButton.tap()
+ barButton.forceTap()
XCTAssertTrue(app.buttons[VectorL10n.signOut].exists)
XCTAssertTrue(app.buttons[VectorL10n.manageSessionRename].exists)
}
diff --git a/RiotSwiftUI/target.yml b/RiotSwiftUI/target.yml
index d7f17f313f..ca8d623c7d 100644
--- a/RiotSwiftUI/target.yml
+++ b/RiotSwiftUI/target.yml
@@ -65,7 +65,7 @@ targets:
- path: ../Riot/Categories/Codable.swift
- path: ../Riot/Assets/en.lproj/Vector.strings
- path: ../Riot/Modules/Analytics/AnalyticsScreen.swift
- - path: ../Riot/Modules/LocationSharing/LocationAuthorizationStatus.swift
+ - path: ../Riot/Modules/LocationSharing/
- path: ../Riot/Modules/QRCode/QRCodeGenerator.swift
- path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfoState.swift
- path: ../Riot/Assets/en.lproj/Untranslated.strings
diff --git a/RiotTests/DecryptionFailureTrackerTests.swift b/RiotTests/DecryptionFailureTrackerTests.swift
new file mode 100644
index 0000000000..7cd9bf480f
--- /dev/null
+++ b/RiotTests/DecryptionFailureTrackerTests.swift
@@ -0,0 +1,341 @@
+//
+// Copyright 2024 New Vector Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+import XCTest
+@testable import Element
+
+
+class DecryptionFailureTrackerTests: XCTestCase {
+
+ class TimeShifter: TimeProvider {
+
+ var timestamp = TimeInterval(0)
+
+ func nowTs() -> TimeInterval {
+ return timestamp
+ }
+ }
+
+ class AnalyticsDelegate : E2EAnalytics {
+ var reportedFailure: Element.DecryptionFailure?;
+
+ func trackE2EEError(_ reason: Element.DecryptionFailure) {
+ reportedFailure = reason
+ }
+
+ }
+
+ let timeShifter = TimeShifter()
+
+ func test_grace_period() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ timeShifter.timestamp = TimeInterval(2)
+
+ // simulate decrypted in the grace period
+ NotificationCenter.default.post(name: .mxEventDidDecrypt, object: fakeEvent)
+
+ decryptionFailureTracker.checkFailures();
+
+ XCTAssertNil(testDelegate.reportedFailure);
+
+ // Pass the grace period
+ timeShifter.timestamp = TimeInterval(5)
+
+ decryptionFailureTracker.checkFailures();
+ XCTAssertNil(testDelegate.reportedFailure);
+
+ }
+
+ func test_report_ratcheted_key_utd() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorOlmCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Pass the max period
+ timeShifter.timestamp = TimeInterval(70)
+
+ decryptionFailureTracker.checkFailures();
+
+ XCTAssertEqual(testDelegate.reportedFailure?.reason, DecryptionFailureReason.olmIndexError);
+ }
+
+ func test_report_unspecified_error() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorBadRoomCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Pass the max period
+ timeShifter.timestamp = TimeInterval(70)
+
+ decryptionFailureTracker.checkFailures();
+
+ XCTAssertEqual(testDelegate.reportedFailure?.reason, DecryptionFailureReason.unspecified);
+ }
+
+
+
+ func test_do_not_double_report() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Pass the max period
+ timeShifter.timestamp = TimeInterval(70)
+
+ decryptionFailureTracker.checkFailures();
+
+ XCTAssertEqual(testDelegate.reportedFailure?.reason, DecryptionFailureReason.olmKeysNotSent);
+
+ // Try to report again the same event
+ testDelegate.reportedFailure = nil
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+ // Pass the grace period
+ timeShifter.timestamp = TimeInterval(10)
+
+ decryptionFailureTracker.checkFailures();
+
+ XCTAssertNil(testDelegate.reportedFailure);
+ }
+
+
+ func test_ignore_not_member() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ let fakeMembers = FakeRoomMembers()
+ fakeMembers.mockMembers[myUser] = MXMembership.ban
+ fakeRoomState.mockMembers = fakeMembers
+
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Pass the grace period
+ timeShifter.timestamp = TimeInterval(5)
+
+ decryptionFailureTracker.checkFailures();
+
+ XCTAssertNil(testDelegate.reportedFailure);
+ }
+
+
+
+ func test_notification_center() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Shift time below GRACE_PERIOD
+ timeShifter.timestamp = TimeInterval(2)
+
+ // Simulate event gets decrypted
+ NotificationCenter.default.post(name: .mxEventDidDecrypt, object: fakeEvent)
+
+
+ // Shift time after GRACE_PERIOD
+ timeShifter.timestamp = TimeInterval(6)
+
+
+ decryptionFailureTracker.checkFailures();
+
+ // Event should have been graced
+ XCTAssertNil(testDelegate.reportedFailure);
+ }
+
+
+ func test_should_report_late_decrypt() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Simulate succesful decryption after grace period but before max wait
+ timeShifter.timestamp = TimeInterval(20)
+
+ // Simulate event gets decrypted
+ NotificationCenter.default.post(name: .mxEventDidDecrypt, object: fakeEvent)
+
+
+ decryptionFailureTracker.checkFailures();
+
+ // Event should have been reported as a late decrypt
+ XCTAssertEqual(testDelegate.reportedFailure?.reason, DecryptionFailureReason.olmKeysNotSent);
+ XCTAssertEqual(testDelegate.reportedFailure?.timeToDecrypt, TimeInterval(20));
+
+ // Assert that it's converted to millis for reporting
+ let analyticsError = testDelegate.reportedFailure!.toAnalyticsEvent()
+
+ XCTAssertEqual(analyticsError.timeToDecryptMillis, 20000)
+
+ }
+
+
+
+ func test_should_report_permanent_decryption_error() {
+
+ let myUser = "test@example.com";
+
+ let decryptionFailureTracker = DecryptionFailureTracker();
+ decryptionFailureTracker.timeProvider = timeShifter;
+
+ let testDelegate = AnalyticsDelegate();
+
+ decryptionFailureTracker.delegate = testDelegate;
+
+ timeShifter.timestamp = TimeInterval(0)
+
+ let fakeEvent = FakeEvent(id: "$0000");
+ fakeEvent.decryptionError = NSError(domain: MXDecryptingErrorDomain, code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue))
+
+
+ let fakeRoomState = FakeRoomState();
+ fakeRoomState.mockMembers = FakeRoomMembers(joined: [myUser])
+
+ decryptionFailureTracker.reportUnableToDecryptError(forEvent: fakeEvent, withRoomState: fakeRoomState, myUser: myUser);
+
+ // Simulate succesful decryption after max wait
+ timeShifter.timestamp = TimeInterval(70)
+
+ decryptionFailureTracker.checkFailures();
+
+ // Event should have been reported as a late decrypt
+ XCTAssertEqual(testDelegate.reportedFailure?.reason, DecryptionFailureReason.olmKeysNotSent);
+ XCTAssertNil(testDelegate.reportedFailure?.timeToDecrypt);
+
+
+ // Assert that it's converted to -1 for reporting
+ let analyticsError = testDelegate.reportedFailure!.toAnalyticsEvent()
+
+ XCTAssertEqual(analyticsError.timeToDecryptMillis, -1)
+
+ }
+}
+
diff --git a/RiotTests/FakeUtils.swift b/RiotTests/FakeUtils.swift
new file mode 100644
index 0000000000..7bd350e4bc
--- /dev/null
+++ b/RiotTests/FakeUtils.swift
@@ -0,0 +1,109 @@
+//
+// Copyright 2024 New Vector Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+
+class FakeEvent: MXEvent {
+
+ var mockEventId: String;
+ var mockSender: String!;
+ var mockDecryptionError: Error?
+
+ init(id: String) {
+ mockEventId = id
+ super.init()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError()
+ }
+
+ override var sender: String! {
+ get { return mockSender }
+ set { mockSender = newValue }
+ }
+
+ override var eventId: String! {
+ get { return mockEventId }
+ set { mockEventId = newValue }
+ }
+
+ override var decryptionError: Error? {
+ get { return mockDecryptionError }
+ set { mockDecryptionError = newValue }
+ }
+
+}
+
+
+class FakeRoomState: MXRoomState {
+
+ var mockMembers: MXRoomMembers?
+
+ override var members: MXRoomMembers? {
+ get { return mockMembers }
+ set { mockMembers = newValue }
+ }
+
+}
+
+class FakeRoomMember: MXRoomMember {
+ var mockMembership: MXMembership = MXMembership.join
+ var mockUserId: String!
+ var mockMembers: MXRoomMembers? = FakeRoomMembers()
+
+ init(mockUserId: String!) {
+ self.mockUserId = mockUserId
+ super.init()
+ }
+
+ override var membership: MXMembership {
+ get { return mockMembership }
+ set { mockMembership = newValue }
+ }
+
+ override var userId: String!{
+ get { return mockUserId }
+ set { mockUserId = newValue }
+ }
+
+}
+
+
+class FakeRoomMembers: MXRoomMembers {
+
+ var mockMembers = [String : MXMembership]()
+
+ init(joined: [String] = [String]()) {
+ for userId in joined {
+ self.mockMembers[userId] = MXMembership.join
+ }
+ super.init()
+ }
+
+ override func member(withUserId userId: String!) -> MXRoomMember? {
+ let membership = mockMembers[userId]
+ if membership != nil {
+ let mockMember = FakeRoomMember(mockUserId: userId)
+ mockMember.mockMembership = membership!
+ return mockMember
+ } else {
+ return nil
+ }
+ }
+
+}
diff --git a/TCHAP_CHANGES.md b/TCHAP_CHANGES.md
index 1fb7fedd31..34a49f547b 100644
--- a/TCHAP_CHANGES.md
+++ b/TCHAP_CHANGES.md
@@ -1,3 +1,19 @@
+## Changes in 2.7.3 (2024-04-02)
+
+đ Improvements
+
+- Rename RiotNSE extension product name to TchapNSE. It will change user-agent of requests in backend logs. ([#664](https://github.com/tchapgouv/tchap-ios/issues/664))
+- RĂ©activation de l'antivirus ([#887](https://github.com/tchapgouv/tchap-ios/issues/887))
+- Activation du partage de géolocalisation ([#970](https://github.com/tchapgouv/tchap-ios/issues/970))
+- Mettre une icÎne plus adaptée sur le bouton "Signaler un problÚme" lors d'un appel VoIP ([#974](https://github.com/tchapgouv/tchap-ios/issues/974))
+- Changement de la formulation du bouton des réglages pour autoriser les notifications sur l'appareil. Ajout d'un texte explicatif. ([#975](https://github.com/tchapgouv/tchap-ios/issues/975))
+- Changer le message d'erreur affiché en cas de problÚme de déchiffrement ([#976](https://github.com/tchapgouv/tchap-ios/issues/976))
+- Modifier l'intitulé de désactivation de compte ([#982](https://github.com/tchapgouv/tchap-ios/issues/982))
+- Utiliser le bouton de validation Tchap en Ă©dition de message. ([#986](https://github.com/tchapgouv/tchap-ios/issues/986))
+- La "notification par email" est rendue disponible Ă tout le monde. ([#995](https://github.com/tchapgouv/tchap-ios/issues/995))
+- Ajout des textes d'alerte d'activation de la géolocalisation dans les targets Btchap et DevTchap ([#998](https://github.com/tchapgouv/tchap-ios/issues/998))
+
+
## Changes in 2.7.2 (2024-02-26)
đ Improvements
diff --git a/Tchap/Assets/Localizations/fr.lproj/Tchap.strings b/Tchap/Assets/Localizations/fr.lproj/Tchap.strings
index d4b803f5fb..faf42d7c05 100644
--- a/Tchap/Assets/Localizations/fr.lproj/Tchap.strings
+++ b/Tchap/Assets/Localizations/fr.lproj/Tchap.strings
@@ -247,10 +247,13 @@
////////////////////////////////////////////////////////////////////////////////
// MARK: Expired Account
-"expired_account_alert_message" = "La durĂ©e de validitĂ© de votre compte a expirĂ©. Un email vous a Ă©tĂ© envoyĂ© pour la renouveler. Une fois que vous aurez suivi le lien quâil contient, cliquez ci-dessous.";
-"expired_account_resume_button" = "Jâai renouvelĂ© mon compte";
-"expired_account_request_renewal_email_button" = "Demander lâenvoi dâun nouvel email";
-"expired_account_on_new_sent_email_msg" = "Un nouvel email vous a Ă©tĂ© envoyĂ© pour renouveler la validitĂ© de votre compte. Une fois que vous aurez suivi le lien quâil contient, cliquez ci-dessous.";
+"expired_account_alert_title" = "Votre compte a expiré";
+"expired_account_alert_message" = "Un email vous a Ă©tĂ© envoyĂ© pour renouveler votre compte. Une fois que vous aurez suivi le lien quâil contient, cliquez ci-dessous.";
+"expired_account_resume_button" = "Continuer";
+"expired_account_request_renewal_email_button" = "Emvoyer un nouvel email";
+"expired_account_on_new_sent_email_title" = "Email envoyé";
+"expired_account_on_new_sent_email_message" = "Un nouvel email vous a Ă©tĂ© envoyĂ© pour renouveler votre compte. Une fois que vous aurez suivi le lien quâil contient, cliquez ci-dessous.";
+"expired_account_on_new_sent_email_button" = "Continuer";
////////////////////////////////////////////////////////////////////////////////
// MARK: Change password
@@ -316,22 +319,33 @@
////////////////////////////////////////////////////////////////////////////////
// MARK: Room Decryption error
-"room_decryption_error_faq_link_message" = "Sinon, consulter cet article de FAQ.";
+"room_decryption_error_faq_link_message" = "En savoir plus.";
////////////////////////////////////////////////////////////////////////////////
// MARK: Room Invite
-"room_invite_error_action_forbidden" = "Cet utilisateur n'est pas autorisé à rejoindre ce salon.";
+"room_invite_error_action_forbidden" = "Cet utilisateur est déjà membre du salon ou n'est pas autorisé à le rejoindre.";
"room_invite_search_consign" = "Veuillez saisir le nom d'un correspondant pour le rechercher dans l'annuaire";
////////////////////////////////////////////////////////////////////////////////
// MARK: Room Invite
"security_cross_signing_setup_title" = "Activer la signature croisée";
-"security_cross_signing_reset_title" = "Ătes-vous sur ?";
+"security_cross_signing_reset_title" = "Ătes-vous sĂ»r ?";
"security_cross_signing_reset_message" = "Faites cette opération seulement si vous avez perdu tous vos autres appareils vérifiés.";
"security_cross_signing_reset_action_title" = "RĂ©initialiser";
+////////////////////////////////////////////////////////////////////////////////
+// MARK: Room send file
+"room_send_file_too_big_title" = "Erreur d'envoi";
+"room_send_file_too_big_message" = "Le fichier est trop lourd pour ĂȘtre envoyĂ©. La taille limite est de %ldMo, mais la taille de votre fichier est de %ldMo.";
+
////////////////////////////////////////////////////////////////////////////////
// MARK: VoIP
"event_formatter_report_incident" = "Signaler un problĂšme";
"void_report_incident_title" = "Signaler un problĂšme VoIP";
"void_report_incident_description" = "Vous avez rencontré un souci durant votre appel VoIP. Dites-nous ce qui s'est passé :";
+
+////////////////////////////////////////////////////////////////////////////////
+// MARK: Settings
+"settings_enable_push_notif_text" = "Sans cette autorisation, les appels entrants ne seront pas notifiés.";
+"settings_enable_email_notif_text" = "Recevez un e-mail si au moins un message récent non lu pendant 72h.";
+"settings_enable_email_notif_link" = "En savoir plus.";
diff --git a/Tchap/Config/BuildSettings.swift b/Tchap/Config/BuildSettings.swift
index b5b7bd85ca..51a0c2008c 100644
--- a/Tchap/Config/BuildSettings.swift
+++ b/Tchap/Config/BuildSettings.swift
@@ -267,19 +267,23 @@ final class BuildSettings: NSObject {
static let tchapFeatureNotificationByEmail = "tchapFeatureNotificationByEmail"
static let tchapFeatureVoiceOverIP = "tchapFeatureVoiceOverIP"
static let tchapFeatureVideoOverIP = "tchapFeatureVideoOverIP"
+ static let tchapFeatureGeolocationSharing = "tchapFeatureGeolocationSharing" // linked to `locationSharingEnabled` property (see above)
static var tchapFeaturesAllowedHomeServersForFeature: [String: [String]] = [
tchapFeatureNotificationByEmail: [
- "agent.dinum.tchap.gouv.fr"
+ tchapFeatureAnyHomeServer
],
tchapFeatureVoiceOverIP: [
"agent.dinum.tchap.gouv.fr",
"agent.diplomatie.tchap.gouv.fr",
"agent.finances.tchap.gouv.fr"
- ]
+ ],
// No activation of video calls actually in Tchap Production.
// tchapFeatureVideoOverIP: [
// "agent.dinum.tchap.gouv.fr"
// ],
+ tchapFeatureGeolocationSharing: [
+ tchapFeatureAnyHomeServer
+ ]
]
// MARK: - Side Menu
@@ -454,9 +458,14 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
/// Overwritten by the home server's .well-known configuration (if any exists)
- static let defaultTileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=")!
-
- static let locationSharingEnabled = false // Currently disabled in Tchap.
+ // Tchap: handle different map providers.
+ private enum TchapMapProvider: String {
+ case geoDataGouv = "https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
+ case ign = "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json"
+ }
+ static let defaultTileServerMapStyleURL = URL(string: TchapMapProvider.geoDataGouv.rawValue)!
+
+ static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
diff --git a/Tchap/Generated/InfoPlist.swift b/Tchap/Generated/InfoPlist.swift
index d65235e8cb..fd4913b7ce 100644
--- a/Tchap/Generated/InfoPlist.swift
+++ b/Tchap/Generated/InfoPlist.swift
@@ -30,6 +30,8 @@ internal enum InfoPlist {
internal static let nsCameraUsageDescription: String = _document["NSCameraUsageDescription"]
internal static let nsContactsUsageDescription: String = _document["NSContactsUsageDescription"]
internal static let nsFaceIDUsageDescription: String = _document["NSFaceIDUsageDescription"]
+ internal static let nsLocationAlwaysAndWhenInUseUsageDescription: String = _document["NSLocationAlwaysAndWhenInUseUsageDescription"]
+ internal static let nsLocationWhenInUseUsageDescription: String = _document["NSLocationWhenInUseUsageDescription"]
internal static let nsMicrophoneUsageDescription: String = _document["NSMicrophoneUsageDescription"]
internal static let nsPhotoLibraryUsageDescription: String = _document["NSPhotoLibraryUsageDescription"]
internal static let uiBackgroundModes: [String] = _document["UIBackgroundModes"]
diff --git a/Tchap/Generated/Strings.swift b/Tchap/Generated/Strings.swift
index dd83433c6e..9f27d360de 100644
--- a/Tchap/Generated/Strings.swift
+++ b/Tchap/Generated/Strings.swift
@@ -271,6 +271,10 @@ public class TchapL10n: NSObject {
public static var errorTitleDefault: String {
return TchapL10n.tr("Tchap", "error_title_default")
}
+ /// Signaler un problĂšme
+ public static var eventFormatterReportIncident: String {
+ return TchapL10n.tr("Tchap", "event_formatter_report_incident")
+ }
/// La durĂ©e de validitĂ© de votre compte a expirĂ©. Un email vous a Ă©tĂ© envoyĂ© pour la renouveler. Une fois que vous aurez suivi le lien quâil contient, cliquez ci-dessous.
public static var expiredAccountAlertMessage: String {
return TchapL10n.tr("Tchap", "expired_account_alert_message")
@@ -563,7 +567,7 @@ public class TchapL10n: NSObject {
public static var roomCreationTitle: String {
return TchapL10n.tr("Tchap", "room_creation_title")
}
- /// Sinon, consulter cet article de FAQ.
+ /// En savoir plus.
public static var roomDecryptionErrorFaqLinkMessage: String {
return TchapL10n.tr("Tchap", "room_decryption_error_faq_link_message")
}
@@ -743,7 +747,7 @@ public class TchapL10n: NSObject {
public static var securityCrossSigningResetMessage: String {
return TchapL10n.tr("Tchap", "security_cross_signing_reset_message")
}
- /// Ătes-vous sur ?
+ /// Ătes-vous sĂ»r ?
public static var securityCrossSigningResetTitle: String {
return TchapL10n.tr("Tchap", "security_cross_signing_reset_title")
}
@@ -791,6 +795,10 @@ public class TchapL10n: NSObject {
public static var settingsCryptoImportInvalidFile: String {
return TchapL10n.tr("Tchap", "settings_crypto_import_invalid_file")
}
+ /// Sans cette autorisation, les appels entrants ne seront pas notifiés.
+ public static var settingsEnablePushNotifText: String {
+ return TchapL10n.tr("Tchap", "settings_enable_push_notif_text")
+ }
/// Les autres utilisateurs ne pourront pas découvrir mon compte lors de leurs recherches
public static var settingsHideFromUsersDirectorySummary: String {
return TchapL10n.tr("Tchap", "settings_hide_from_users_directory_summary")
@@ -855,6 +863,14 @@ public class TchapL10n: NSObject {
public static var tchapRoomInvalidLink: String {
return TchapL10n.tr("Tchap", "tchap_room_invalid_link")
}
+ /// Vous avez rencontré un souci durant votre appel VoIP. Dites-nous ce qui s'est passé :
+ public static var voidReportIncidentDescription: String {
+ return TchapL10n.tr("Tchap", "void_report_incident_description")
+ }
+ /// Signaler un problĂšme VoIP
+ public static var voidReportIncidentTitle: String {
+ return TchapL10n.tr("Tchap", "void_report_incident_title")
+ }
/// Attention
public static var warningTitle: String {
return TchapL10n.tr("Tchap", "warning_title")
diff --git a/Tchap/Modules/RoomPreview/RoomPreviewCoordinator.swift b/Tchap/Modules/RoomPreview/RoomPreviewCoordinator.swift
index c0103af40e..5c345ebed2 100644
--- a/Tchap/Modules/RoomPreview/RoomPreviewCoordinator.swift
+++ b/Tchap/Modules/RoomPreview/RoomPreviewCoordinator.swift
@@ -220,6 +220,15 @@ final class RoomPreviewCoordinator: NSObject, RoomPreviewCoordinatorType {
// MARK: - RoomViewControllerDelegate
extension RoomPreviewCoordinator: RoomViewControllerDelegate {
+
+ func roomViewController(_ roomViewController: RoomViewController, didRequestLiveLocationPresentationForBubbleData bubbleData: MXKRoomBubbleCellDataStoring) {
+ //
+ }
+
+ func roomViewControllerDidStopLiveLocationSharing(_ roomViewController: RoomViewController, beaconInfoEventId: String?) {
+ //
+ }
+
func roomViewController(_ roomViewController: RoomViewController, showRoomWithId roomID: String, eventId eventID: String?) {
//
}
diff --git a/Tchap/SupportingFiles/Info.plist b/Tchap/SupportingFiles/Info.plist
index 87e719493e..666cd98359 100644
--- a/Tchap/SupportingFiles/Info.plist
+++ b/Tchap/SupportingFiles/Info.plist
@@ -53,12 +53,17 @@
In order to show who among your contacts already uses Tchap, we can exploit the e-mail addresses of your address book. These data will not be stored. For more information, please visit the privacy policy page in the app settings
NSFaceIDUsageDescription
Face ID is used to access your app.
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ When you share your location to people, Tchap needs access to show them a map.
+ NSLocationWhenInUseUsageDescription
+ When you share your location to people, Tchap needs access to show them a map.
NSMicrophoneUsageDescription
Tchap needs to access your microphone to take videos, and record voice messages.
NSPhotoLibraryUsageDescription
This allows you to select pictures or videos from the photo library, and send them in your conversations. You can also use one of these pictures to set your profile picture.
UIBackgroundModes
+ location
remote-notification
voip
diff --git a/Tchap/SupportingFiles/PrivacyInfo.xcprivacy b/Tchap/SupportingFiles/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..500ae9affe
--- /dev/null
+++ b/Tchap/SupportingFiles/PrivacyInfo.xcprivacy
@@ -0,0 +1,41 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ 1C8F.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ 7D9E.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 3D61.1
+
+
+
+
+
diff --git a/Tchap/target.yml b/Tchap/target.yml
index b94a4cfd3c..9f497793c1 100644
--- a/Tchap/target.yml
+++ b/Tchap/target.yml
@@ -53,6 +53,7 @@ targetTemplates:
- package: SwiftOGG
- package: WysiwygComposer
- package: AnalyticsEvents
+ - package: Mapbox
preBuildScripts:
- name: đ Environment
@@ -245,6 +246,7 @@ targetTemplates:
- path: ../Riot/Modules/KeyBackup
- path: ../Riot/Modules/KeyVerification
- path: ../Riot/Modules/LaunchLoading
+ - path: ../Riot/Modules/LocationSharing
- path: ../Riot/Modules/MatrixKit
- path: ../Riot/Modules/MediaPicker
- path: ../Riot/Modules/MediaPickerV2
@@ -257,13 +259,13 @@ targetTemplates:
- path: ../Riot/Modules/Rendezvous
- path: ../Riot/Modules/Room
excludes:
- - "Location"
- - "RoomViewController+LocationSharing.swift"
- - "TimelineCells/LocationView"
- - "TimelineCells/Styles/Plain/Cells/Location"
- - "TimelineCells/Styles/Bubble/Cells/Location"
- - "Views/BubbleCells/KeyVerification/SizingViewHeight.swift"
- - "Views/BubbleCells/Location"
+# - "Location"
+# - "RoomViewController+LocationSharing.swift"
+# - "TimelineCells/LocationView"
+# - "TimelineCells/Styles/Plain/Cells/Location"
+# - "TimelineCells/Styles/Bubble/Cells/Location"
+# - "Views/BubbleCells/KeyVerification/SizingViewHeight.swift"
+# - "Views/BubbleCells/Location"
- path: ../Riot/Modules/Rooms
- path: ../Riot/Modules/Secrets
- path: ../Riot/Modules/SecureBackup
@@ -294,7 +296,7 @@ targetTemplates:
excludes:
- "**/Test/**"
- "Common/Locale/LocaleProvider.swift"
- - "LocationSharing"
+# - "LocationSharing"
- "Room/LiveLocationSharingViewer"
- "Room/LocationSharing"
- "Room/StaticLocationSharingViewer"
diff --git a/TchapNSE/Common.xcconfig b/TchapNSE/Common.xcconfig
index 5fb618d128..19b42ed0c8 100644
--- a/TchapNSE/Common.xcconfig
+++ b/TchapNSE/Common.xcconfig
@@ -19,7 +19,10 @@
#include "Tchap/SupportingFiles/App-Common.xcconfig"
-PRODUCT_NAME = RiotNSE
+
+// Tchap: Customize TchapNSE user-agent for back-end logs.
+// PRODUCT_NAME = RiotNSE
+PRODUCT_NAME = TchapNSE
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).nse
INFOPLIST_FILE = RiotNSE/Info.plist
@@ -27,5 +30,8 @@ INFOPLIST_FILE = RiotNSE/Info.plist
CODE_SIGN_ENTITLEMENTS = RiotNSE/RiotNSE.entitlements
SKIP_INSTALL = YES
-SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotNSE-Bridging-Header.h
+
+// Tchap: Customize TchapNSE user-agent for back-end logs.
+// SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotNSE-Bridging-Header.h
+SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h
diff --git a/changelog.d/1000.change b/changelog.d/1000.change
new file mode 100644
index 0000000000..83cb7789ff
--- /dev/null
+++ b/changelog.d/1000.change
@@ -0,0 +1 @@
+Afficher le salon "Tchap Annonces" dans le flux normal des salons
\ No newline at end of file
diff --git a/changelog.d/1002.change b/changelog.d/1002.change
new file mode 100644
index 0000000000..99144a92cc
--- /dev/null
+++ b/changelog.d/1002.change
@@ -0,0 +1 @@
+Ajout d'un "Privacy Manifest" dans le projet Xcode (obligatoire Ă partir du 1er mai 2024)
\ No newline at end of file
diff --git a/changelog.d/1009.change b/changelog.d/1009.change
new file mode 100644
index 0000000000..d09358cb85
--- /dev/null
+++ b/changelog.d/1009.change
@@ -0,0 +1 @@
+L'affichage de géolocalisation plante quand l'affichage en bulle est désactivé
\ No newline at end of file
diff --git a/changelog.d/1015.change b/changelog.d/1015.change
new file mode 100644
index 0000000000..9dd68d1fa8
--- /dev/null
+++ b/changelog.d/1015.change
@@ -0,0 +1 @@
+Afficher un message d'alerte avant envoi d'une piĂšce jointe trop lourde.
\ No newline at end of file
diff --git a/changelog.d/1017.change b/changelog.d/1017.change
new file mode 100644
index 0000000000..bb319aecfb
--- /dev/null
+++ b/changelog.d/1017.change
@@ -0,0 +1 @@
+Ajout de la géolocalisation en background sur les targets Dev et Pre-prod
\ No newline at end of file
diff --git a/changelog.d/1019.change b/changelog.d/1019.change
new file mode 100644
index 0000000000..d26acdd760
--- /dev/null
+++ b/changelog.d/1019.change
@@ -0,0 +1 @@
+Mauvais libellé de fermeture de compte en anglais.
\ No newline at end of file
diff --git a/changelog.d/1021.change b/changelog.d/1021.change
new file mode 100644
index 0000000000..f56b6a6b2f
--- /dev/null
+++ b/changelog.d/1021.change
@@ -0,0 +1 @@
+Améliorer les textes du parcours de renouvellement de compte.
\ No newline at end of file
diff --git a/changelog.d/1022.change b/changelog.d/1022.change
new file mode 100644
index 0000000000..56a47497b4
--- /dev/null
+++ b/changelog.d/1022.change
@@ -0,0 +1 @@
+Mauvais message d'erreur quand on invite un utilisateur déjà présent dans un salon
\ No newline at end of file
diff --git a/changelog.d/1027.change b/changelog.d/1027.change
new file mode 100644
index 0000000000..f56b6a6b2f
--- /dev/null
+++ b/changelog.d/1027.change
@@ -0,0 +1 @@
+Améliorer les textes du parcours de renouvellement de compte.
\ No newline at end of file
diff --git a/changelog.d/1029.change b/changelog.d/1029.change
new file mode 100644
index 0000000000..59871594a9
--- /dev/null
+++ b/changelog.d/1029.change
@@ -0,0 +1 @@
+Améliorer la compréhension du fonctionnement des notifications par email.
\ No newline at end of file
diff --git a/changelog.d/832.change b/changelog.d/832.change
new file mode 100644
index 0000000000..72cdd7a085
--- /dev/null
+++ b/changelog.d/832.change
@@ -0,0 +1 @@
+Limiter la longueur du message d'origine à l'affichage d'une réponse avec citation.
\ No newline at end of file
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 973e2ea63f..79cea6c24b 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -21,7 +21,7 @@ platform :ios do
before_all do
# Ensure used Xcode version
- xcversion(version: "14.2")
+ xcversion(version: "15.2")
end
#### Public ####
@@ -142,7 +142,7 @@ platform :ios do
run_tests(
workspace: "Tchap.xcworkspace",
scheme: "RiotSwiftUITests",
- device: "iPhone 14",
+ device: "iPhone 15",
code_coverage: true,
# Test result configuration
result_bundle: true,
diff --git a/project.yml b/project.yml
index c34413a687..60a1bfb830 100644
--- a/project.yml
+++ b/project.yml
@@ -52,7 +52,7 @@ include:
packages:
AnalyticsEvents:
url: https://github.com/matrix-org/matrix-analytics-events
- exactVersion: 0.5.0
+ exactVersion: 0.15.0
Mapbox:
url: https://github.com/maplibre/maplibre-gl-native-distribution
minVersion: 5.12.2
@@ -66,7 +66,7 @@ packages:
branch: 0.0.1
WysiwygComposer:
url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift
- version: 2.19.0
+ version: 2.29.0
DeviceKit:
url: https://github.com/devicekit/DeviceKit
majorVersion: 4.7.0