diff --git a/.gitattributes b/.gitattributes
index 0542767eff..2062142284 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,2 @@
**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
+**/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7d895d0fda..9579e81997 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,9 +2,10 @@ name: APK Build
on:
workflow_dispatch:
- pull_request: { }
+ pull_request:
+ merge_group:
push:
- branches: [ main, develop ]
+ branches: [ develop ]
# Enrich gradle.properties for CI/CD
env:
@@ -13,14 +14,17 @@ env:
jobs:
debug:
- name: Build debug APKs
+ name: Build APKs
runs-on: ubuntu-latest
- if: github.ref != 'refs/heads/main'
+ # Skip for `main` and the merge queue if the branch is up to date with `develop`
+ if: github.ref != 'refs/heads/main' && github.event.merge_group.base_ref != 'refs/heads/develop'
strategy:
+ matrix:
+ variant: [debug, release, nightly, samples]
fail-fast: false
# Allow all jobs on develop. Just one per PR.
concurrency:
- group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}', github.sha) || format('build-debug-{0}', github.ref) }}
+ group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}-{1}', matrix.variant, github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
@@ -34,14 +38,18 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.6.1
+ uses: gradle/gradle-build-action@v2.7.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APK
+ if: ${{ matrix.variant == 'debug' }}
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
- run: ./gradlew assembleDebug $CI_GRADLE_ARG_PROPERTIES
- - name: Upload debug APKs
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
+ run: ./gradlew assembleDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
+ - name: Upload APK APKs
+ if: ${{ matrix.variant == 'debug' }}
uses: actions/upload-artifact@v3
with:
name: elementx-debug
@@ -53,12 +61,12 @@ jobs:
continue-on-error: true
env:
token: ${{ secrets.DIAWI_TOKEN }}
- if: ${{ github.event_name == 'pull_request' && env.token != '' }}
+ if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && env.token != '' }}
with:
token: ${{ env.token }}
file: app/build/outputs/apk/debug/app-arm64-v8a-debug.apk
- name: Add or update PR comment with QR Code to download APK.
- if: ${{ github.event_name == 'pull_request' && steps.diawi.conclusion == 'success' }}
+ if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && steps.diawi.conclusion == 'success' }}
uses: NejcZdovc/comment-pr@v2
with:
message: |
@@ -70,8 +78,11 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Compile release sources
- run: ./gradlew compileReleaseSources $CI_GRADLE_ARG_PROPERTIES
+ if: ${{ matrix.variant == 'release' }}
+ run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
- name: Compile nightly sources
- run: ./gradlew compileNightlySources $CI_GRADLE_ARG_PROPERTIES
+ if: ${{ matrix.variant == 'nightly' }}
+ run: ./gradlew compileNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
- name: Compile samples minimal
+ if: ${{ matrix.variant == 'samples' }}
run: ./gradlew :samples:minimal:assemble $CI_GRADLE_ARG_PROPERTIES
diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index 223a273b68..4d997ec632 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -1,17 +1,19 @@
name: Danger CI
-on: [pull_request]
+on: [pull_request, merge_group]
jobs:
build:
runs-on: ubuntu-latest
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
name: Danger main check
steps:
- uses: actions/checkout@v3
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
- uses: danger/danger-js@11.2.6
+ uses: danger/danger-js@11.2.8
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
index 7b68c0077d..c1e478b15c 100644
--- a/.github/workflows/gradle-wrapper-validation.yml
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -1,12 +1,15 @@
name: "Validate Gradle Wrapper"
on:
- pull_request: { }
+ pull_request:
+ merge_group:
push:
branches: [ main, develop ]
jobs:
validation:
name: "Validation"
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
runs-on: ubuntu-latest
# No concurrency required, this is a prerequisite to other actions and should run every time.
steps:
diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml
index 0349e373bb..74fb1cfc83 100644
--- a/.github/workflows/maestro.yml
+++ b/.github/workflows/maestro.yml
@@ -38,16 +38,14 @@ jobs:
run: ./gradlew assembleDebug $CI_GRADLE_ARG_PROPERTIES
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
- - name: Upload debug APKs
- uses: actions/upload-artifact@v3
- with:
- name: elementx-debug
- path: |
- app/build/outputs/apk/debug/*.apk
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
- uses: mobile-dev-inc/action-maestro-cloud@v1.4.1
with:
api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }}
- app-file: app/build/outputs/apk/debug/app-universal-debug.apk
+ # Doc says (https://github.com/mobile-dev-inc/action-maestro-cloud#android):
+ # app-file should point to an x86 compatible APK file, so upload the x86_64 one (much smaller than the universal APK).
+ app-file: app/build/outputs/apk/debug/app-x86_64-debug.apk
env: |
USERNAME=maestroelement
PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }}
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 95c2deb8eb..85c9ed1422 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -1,4 +1,4 @@
-name: Build and release nightly APK
+name: Build and release nightly application
on:
workflow_dispatch:
@@ -12,7 +12,7 @@ env:
jobs:
nightly:
- name: Build and publish nightly APK to Firebase
+ name: Build and publish nightly bundle to Firebase
runs-on: ubuntu-latest
if: ${{ github.repository == 'vector-im/element-x-android' }}
steps:
@@ -31,18 +31,21 @@ jobs:
sed 's/CHANGES\.md/CHANGES_NIGHTLY\.md/' towncrier.toml.bak > towncrier.toml
rm towncrier.toml.bak
yes n | towncrier build --version nightly
- - name: Build and upload Nightly APK
+ - name: Build and upload Nightly application
run: |
./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }}
ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }}
ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }}
FIREBASE_TOKEN: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_FIREBASE_TOKEN }}
- name: Additionally upload Nightly APK to browserstack for testing
continue-on-error: true # don't block anything by this upload failing (for now)
- run: curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly"
+ run: |
+ curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly"
env:
BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }}
BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }}
diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml
index ce7b763ef1..5c70a3d385 100644
--- a/.github/workflows/nightlyReports.yml
+++ b/.github/workflows/nightlyReports.yml
@@ -62,7 +62,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.6.1
+ uses: gradle/gradle-build-action@v2.7.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Dependency analysis
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index 94b8b7ff4e..1efa0ae215 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -2,7 +2,8 @@ name: Code Quality Checks
on:
workflow_dispatch:
- pull_request: { }
+ pull_request:
+ merge_group:
push:
branches: [ main, develop ]
@@ -15,6 +16,8 @@ jobs:
checkScript:
name: Search for forbidden patterns
runs-on: ubuntu-latest
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
steps:
- uses: actions/checkout@v3
- name: Run code quality check suite
@@ -23,6 +26,8 @@ jobs:
check:
name: Project Check Suite
runs-on: ubuntu-latest
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-develop-{0}', github.sha) || format('check-{0}', github.ref) }}
@@ -39,7 +44,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.6.1
+ uses: gradle/gradle-build-action@v2.7.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run code quality check suite
@@ -65,7 +70,7 @@ jobs:
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
- uses: danger/danger-js@11.2.6
+ uses: danger/danger-js@11.2.8
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:
diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml
index d088b3ad94..54e35bbaff 100644
--- a/.github/workflows/recordScreenshots.yml
+++ b/.github/workflows/recordScreenshots.yml
@@ -24,7 +24,7 @@ jobs:
java-version: '17'
# Add gradle cache, this should speed up the process
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.6.1
+ uses: gradle/gradle-build-action@v2.7.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Record screenshots
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..8ab294bc72
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,40 @@
+name: Create release App Bundle
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ main ]
+
+# Enrich gradle.properties for CI/CD
+env:
+ GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
+ CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon
+
+jobs:
+ release:
+ name: Create App Bundle
+ runs-on: ubuntu-latest
+ concurrency:
+ group: ${{ github.ref == 'refs/head/main' && format('build-release-main-{0}', github.sha) }}
+ cancel-in-progress: true
+ steps:
+ - uses: actions/checkout@v3
+ - name: Use JDK 17
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin' # See 'Supported distributions' for available options
+ java-version: '17'
+ - name: Configure gradle
+ uses: gradle/gradle-build-action@v2.7.0
+ - name: Create app bundle
+ env:
+ ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
+ ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
+ ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
+ run: ./gradlew bundleRelease $CI_GRADLE_ARG_PROPERTIES
+ - name: Upload bundle as artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: elementx-app-bundle-unsigned
+ path: |
+ app/build/outputs/bundle/release/app-release.aab
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
new file mode 100644
index 0000000000..24e5b5ad9b
--- /dev/null
+++ b/.github/workflows/sonar.yml
@@ -0,0 +1,51 @@
+name: Code Quality Checks
+
+on:
+ workflow_dispatch:
+ pull_request:
+ merge_group:
+ push:
+ branches: [ main, develop ]
+
+# Enrich gradle.properties for CI/CD
+env:
+ GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -XX:MaxMetaspaceSize=512m -Dkotlin.daemon.jvm.options="-Xmx2g" -Dkotlin.incremental=false
+ CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon --warn
+
+jobs:
+ sonar:
+ name: Project Check Suite
+ runs-on: ubuntu-latest
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
+ # Allow all jobs on main and develop. Just one per PR.
+ concurrency:
+ group: ${{ github.ref == 'refs/heads/main' && format('sonar-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('sonar-develop-{0}', github.sha) || format('sonar-{0}', github.ref) }}
+ cancel-in-progress: true
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ # Ensure we are building the branch and not the branch after being merged on develop
+ # https://github.com/actions/checkout/issues/881
+ ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
+ - name: Use JDK 17
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin' # See 'Supported distributions' for available options
+ java-version: '17'
+ - name: Configure gradle
+ uses: gradle/gradle-build-action@v2.7.0
+ with:
+ cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
+ - name: 🔊 Publish results to Sonar
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
+ if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
+ run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES
+ - name: Prepare Danger
+ if: always()
+ run: |
+ npm install --save-dev @babel/core
+ npm install --save-dev @babel/plugin-transform-flow-strip-types
+ yarn add danger-plugin-lint-report --dev
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 04fd393e25..97a739f747 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -2,7 +2,8 @@ name: Test
on:
workflow_dispatch:
- pull_request: { }
+ pull_request:
+ merge_group:
push:
branches: [ main, develop ]
@@ -15,6 +16,8 @@ jobs:
tests:
name: Runs unit tests
runs-on: ubuntu-latest
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
# Allow all jobs on main and develop. Just one per PR.
concurrency:
@@ -33,7 +36,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
- uses: gradle/gradle-build-action@v2.6.1
+ uses: gradle/gradle-build-action@v2.7.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
diff --git a/.github/workflows/validate-lfs.yml b/.github/workflows/validate-lfs.yml
index 25fe50359c..63ded8f4e1 100644
--- a/.github/workflows/validate-lfs.yml
+++ b/.github/workflows/validate-lfs.yml
@@ -1,10 +1,12 @@
name: Validate Git LFS
-on: [pull_request]
+on: [pull_request, merge_group]
jobs:
build:
runs-on: ubuntu-latest
+ # Don't run in the merge queue again if the branch is up to date with `develop`
+ if: github.event.merge_group.base_ref != 'refs/heads/develop'
name: Validate
steps:
- uses: nschloe/action-cached-lfs-checkout@v1.2.1
diff --git a/.gitignore b/.gitignore
index 70599626ce..0b61f0aaaf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,7 @@ captures/
# IntelliJ
*.iml
.idea/.name
+.idea/androidTestResultsUserPreferences.xml
.idea/assetWizardSettings.xml
.idea/compiler.xml
.idea/deploymentTargetDropDown.xml
diff --git a/.idea/dictionaries/shared.xml b/.idea/dictionaries/shared.xml
index aafe02a2c8..2fc10f455b 100644
--- a/.idea/dictionaries/shared.xml
+++ b/.idea/dictionaries/shared.xml
@@ -8,6 +8,7 @@
-./gradlew check +./tools/quality/check.shSome separate commands can also be run, see below. +#### detekt + +
+./gradlew detekt ++ #### ktlint
@@ -153,7 +158,7 @@ Make sure the following commands execute without any error:
### Tests
-Element X is currently supported on Android Lollipop (API 21+): please test your change on an Android device (or Android emulator) running with API 21. Many issues can happen (including crashes) on older devices.
+Element X is currently supported on Android Marshmallow (API 23+): please test your change on an Android device (or Android emulator) running with API 23. Many issues can happen (including crashes) on older devices.
Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
You should consider adding Unit tests with your PR, and also integration tests (AndroidTest). Please refer to [this document](./docs/integration_tests.md) to install and run the integration test environment.
@@ -166,7 +171,18 @@ For instance, when updating the image `src` of an ImageView, please also conside
### Jetpack Compose
-When adding or editing `@Composable`, make sure that you create a `@Preview` function, with suffix `Preview`. This will also create a UI test automatically.
+When adding or editing `@Composable`, make sure that you create an internal function annotated with `@DayNightPreviews`, with a name suffixed by `Preview`, and having `ElementPreview` as the root composable.
+
+Example:
+```kotlin
+@DayNightPreviews
+@Composable
+internal fun PinIconPreview() = ElementPreview {
+ PinIcon()
+}
+```
+
+This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team.
### Authors
diff --git a/README.md b/README.md
index e31acf87b8..f24efcd828 100644
--- a/README.md
+++ b/README.md
@@ -3,14 +3,18 @@
[](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[](https://codecov.io/github/vector-im/element-x-android)
-[](https://matrix.to/#/#element-android:matrix.org)
-[](https://translate.element.io/engage/element-android/?utm_source=widget)
+[](https://matrix.to/#/#element-x-android:matrix.org)
+[](https://localazy.com/p/element)
-# element-x-android
+# Element X Android
-ElementX Android is a [Matrix](https://matrix.org/) Android Client provided by [Element](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionality.
+Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities.
-The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using Jetpack compose.
+The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx).
+
+Learn more about why we are building Element X in our blog post: [https://element.io/blog/element-x-experience-the-future-of-element/](https://element.io/blog/element-x-experience-the-future-of-element/).
+
+## Table of contents
@@ -28,24 +32,41 @@ The application is a total rewrite of [Element-Android](https://github.com/vecto
Here are some early screenshots of the application:
-|
|
|
|
|
+
+
+|
|
|
|
|
|-|-|-|-|
+|
|
|
|
|
## Rust SDK
-ElementX leverages the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) through an FFI layer that the final client can directly import and use.
+Element X leverages the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) through an FFI layer that the final client can directly import and use.
We're doing this as a way to share code between platforms and while we've seen promising results it's still in the experimental stage and bound to change.
## Status
-This project is in work in progress. The app does not cover yet all functionalities we expect.
+This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/vector-im/element-x-android/issues/911).
## Contributing
-Please see our [contribution guide](CONTRIBUTING.md).
+Want to get actively involved in the project? You're more than welcome! A good way to start is to check the issues that are labelled with the [good first issue](https://github.com/vector-im/element-x-android/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. Let us know by commenting the issue that you're starting working on it.
-Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-android:matrix.org).
+But first make sure to read our [contribution guide](CONTRIBUTING.md) first.
+
+You can also come chat with the community in the Matrix [room](https://matrix.to/#/#element-x-android:matrix.org) dedicated to the project.
## Build instructions
@@ -54,9 +75,9 @@ Makes sure to select the `app` configuration when building (as we also have samp
## Support
-When you are experiencing an issue on ElementX Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues)
-and then in [#element-android:matrix.org](https://matrix.to/#/#element-android:matrix.org).
-If after your research you still have a question, ask at [#element-android:matrix.org](https://matrix.to/#/#element-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash.
+When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues)
+and then in [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org).
+If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting from the application settings. This is especially recommended when you encounter a crash.
## Copyright & License
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 6a28adecf1..19bb2ea84b 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -129,6 +129,8 @@ android {
// "App Distribution found more than 1 output file for this variant.
// Please contact firebase-support@google.com for help using APK splits with App Distribution."
artifactPath = "$rootDir/app/build/outputs/apk/nightly/app-universal-nightly.apk"
+ // artifactType = "AAB"
+ // artifactPath = "$rootDir/app/build/outputs/bundle/nightly/app-nightly.aab"
// This file will be generated by the GitHub action
releaseNotesFile = "CHANGES_NIGHTLY.md"
groups = "external-testers"
diff --git a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt
index ec3259fb7c..da8592771c 100644
--- a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt
+++ b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt
@@ -24,8 +24,7 @@ import io.element.android.x.di.DaggerAppComponent
import io.element.android.x.info.logApplicationInfo
import io.element.android.x.initializer.CrashInitializer
import io.element.android.x.initializer.EmojiInitializer
-import io.element.android.x.initializer.MatrixInitializer
-import io.element.android.x.initializer.TimberInitializer
+import io.element.android.x.initializer.TracingInitializer
class ElementXApplication : Application(), DaggerComponentOwner {
@@ -39,8 +38,7 @@ class ElementXApplication : Application(), DaggerComponentOwner {
appComponent = DaggerAppComponent.factory().create(applicationContext)
AppInitializer.getInstance(this).apply {
initializeComponent(CrashInitializer::class.java)
- initializeComponent(TimberInitializer::class.java)
- initializeComponent(MatrixInitializer::class.java)
+ initializeComponent(TracingInitializer::class.java)
initializeComponent(EmojiInitializer::class.java)
}
logApplicationInfo()
diff --git a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt
index 4d75d8601e..5fb3523d6e 100644
--- a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt
+++ b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt
@@ -17,11 +17,15 @@
package io.element.android.x.di
import com.squareup.anvil.annotations.ContributesTo
+import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.matrix.api.tracing.TracingService
@ContributesTo(AppScope::class)
interface AppBindings {
fun mainDaggerComponentOwner(): MainDaggerComponentsOwner
fun snackbarDispatcher(): SnackbarDispatcher
+ fun tracingService(): TracingService
+ fun bugReporter(): BugReporter
}
diff --git a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt
index 49c2cc5782..52e3af1aab 100644
--- a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt
+++ b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt
@@ -28,7 +28,7 @@ import io.element.android.x.R
@Preview
@Composable
-fun IconPreview(
+internal fun IconPreview(
modifier: Modifier = Modifier,
) {
Box(modifier = modifier) {
@@ -39,7 +39,7 @@ fun IconPreview(
@Preview
@Composable
-fun RoundIconPreview(
+internal fun RoundIconPreview(
modifier: Modifier = Modifier,
) {
Box(modifier = modifier.clip(shape = CircleShape)) {
diff --git a/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt
deleted file mode 100644
index 5eebc88756..0000000000
--- a/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2022 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.
- */
-
-package io.element.android.x.initializer
-
-import android.content.Context
-import androidx.startup.Initializer
-import io.element.android.libraries.matrix.impl.tracing.setupTracing
-import io.element.android.libraries.matrix.api.tracing.TracingConfigurations
-import io.element.android.x.BuildConfig
-
-class MatrixInitializer : Initializer {
-
- override fun create(context: Context) {
- if (BuildConfig.DEBUG) {
- setupTracing(TracingConfigurations.debug)
- } else {
- setupTracing(TracingConfigurations.release)
- }
- }
-
- override fun dependencies(): List>> = listOf(TimberInitializer::class.java)
-}
diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt
new file mode 100644
index 0000000000..068d439994
--- /dev/null
+++ b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022 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.
+ */
+
+package io.element.android.x.initializer
+
+import android.content.Context
+import androidx.startup.Initializer
+import io.element.android.libraries.architecture.bindings
+import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
+import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations
+import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
+import io.element.android.x.BuildConfig
+import io.element.android.x.di.AppBindings
+import timber.log.Timber
+
+class TracingInitializer : Initializer {
+
+ override fun create(context: Context) {
+ val appBindings = context.bindings()
+ val tracingService = appBindings.tracingService()
+ val bugReporter = appBindings.bugReporter()
+ Timber.plant(tracingService.createTimberTree())
+ val tracingConfiguration = if (BuildConfig.DEBUG) {
+ TracingConfiguration(
+ filterConfiguration = TracingFilterConfigurations.debug,
+ writesToLogcat = true,
+ writesToFilesConfiguration = WriteToFilesConfiguration.Disabled
+ )
+ } else {
+ TracingConfiguration(
+ filterConfiguration = TracingFilterConfigurations.release,
+ writesToLogcat = false,
+ writesToFilesConfiguration = WriteToFilesConfiguration.Enabled(
+ directory = bugReporter.logDirectory().absolutePath,
+ filenamePrefix = "logs"
+ )
+ )
+ }
+ bugReporter.cleanLogDirectoryIfNeeded()
+ tracingService.setupTracing(tracingConfiguration)
+ }
+
+ override fun dependencies(): List>> = mutableListOf()
+}
diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts
index 6abc3c656b..cffd318fb1 100644
--- a/appnav/build.gradle.kts
+++ b/appnav/build.gradle.kts
@@ -65,6 +65,8 @@ dependencies {
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
+ testImplementation(projects.features.networkmonitor.test)
+ testImplementation(projects.tests.testutils)
testImplementation(projects.features.rageshake.test)
testImplementation(projects.features.rageshake.impl)
testImplementation(projects.services.appnavstate.test)
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt
index 64c9ec7c4f..e55f059d14 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt
@@ -58,7 +58,8 @@ class LoggedInEventProcessor @Inject constructor(
.filter { it }
.onEach {
displayMessage(CommonStrings.common_verification_complete)
- }.launchIn(this)
+ }
+ .launchIn(this)
}
}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
index 4130e5da23..7943151a5e 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt
@@ -44,14 +44,14 @@ import io.element.android.appnav.loggedin.LoggedInNode
import io.element.android.appnav.room.RoomFlowNode
import io.element.android.appnav.room.RoomLoadedFlowNode
import io.element.android.features.createroom.api.CreateRoomEntryPoint
+import io.element.android.features.ftue.api.FtueEntryPoint
+import io.element.android.features.ftue.api.state.FtueState
import io.element.android.features.invitelist.api.InviteListEntryPoint
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.features.verifysession.api.VerifySessionEntryPoint
-import io.element.android.features.ftue.api.FtueEntryPoint
-import io.element.android.features.ftue.api.state.FtueState
import io.element.android.libraries.architecture.BackstackNode
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
@@ -69,10 +69,12 @@ import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
+import timber.log.Timber
@ContributesNode(AppScope::class)
class LoggedInFlowNode @AssistedInject constructor(
@@ -100,13 +102,13 @@ class LoggedInFlowNode @AssistedInject constructor(
) {
interface Callback : Plugin {
- fun onOpenBugReport() = Unit
+ fun onOpenBugReport()
}
interface LifecycleCallback : NodeLifecycleCallback {
- fun onFlowCreated(identifier: String, client: MatrixClient) = Unit
+ fun onFlowCreated(identifier: String, client: MatrixClient)
- fun onFlowReleased(identifier: String, client: MatrixClient) = Unit
+ fun onFlowReleased(identifier: String, client: MatrixClient)
}
data class Inputs(
@@ -123,7 +125,6 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun onBuilt() {
super.onBuilt()
-
lifecycle.subscribe(
onCreate = {
plugins().forEach { it.onFlowCreated(id, inputs.matrixClient) }
@@ -138,14 +139,12 @@ class LoggedInFlowNode @AssistedInject constructor(
backstack.push(NavTarget.Ftue)
}
},
- onResume = {
- lifecycleScope.launch {
- syncService.startSync()
+ onStop = {
+ //Counterpart startSync is done in observeSyncStateAndNetworkStatus method.
+ coroutineScope.launch {
+ syncService.stopSync()
}
},
- onPause = {
- syncService.stopSync()
- },
onDestroy = {
plugins().forEach { it.onFlowReleased(id, inputs.matrixClient) }
appNavigationStateService.onLeavingSpace(id)
@@ -153,22 +152,23 @@ class LoggedInFlowNode @AssistedInject constructor(
loggedInFlowProcessor.stopObserving()
}
)
-
observeSyncStateAndNetworkStatus()
}
+ @OptIn(FlowPreview::class)
private fun observeSyncStateAndNetworkStatus() {
lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
combine(
- syncService.syncState,
+ // small debounce to avoid spamming startSync when the state is changing quickly in case of error.
+ syncService.syncState.debounce(100),
networkMonitor.connectivity
) { syncState, networkStatus ->
- syncState == SyncState.Error && networkStatus == NetworkStatus.Online
+ Pair(syncState, networkStatus)
}
- .distinctUntilChanged()
- .collect { restartSync ->
- if (restartSync) {
+ .collect { (syncState, networkStatus) ->
+ Timber.d("Sync state: $syncState, network status: $networkStatus")
+ if (syncState != SyncState.Running && networkStatus == NetworkStatus.Online) {
syncService.startSync()
}
}
@@ -305,7 +305,8 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun onFtueFlowFinished() {
backstack.pop()
}
- }).build()
+ })
+ .build()
}
}
}
@@ -350,3 +351,4 @@ class LoggedInFlowNode @AssistedInject constructor(
backstack.push(NavTarget.InviteList)
}
}
+
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt
index 6a3d8ff9dd..b567395c1e 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt
@@ -21,8 +21,6 @@ import io.element.android.features.login.api.oidc.OidcAction
import io.element.android.features.login.api.oidc.OidcIntentResolver
import io.element.android.libraries.deeplink.DeeplinkData
import io.element.android.libraries.deeplink.DeeplinkParser
-import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.core.SessionId
import timber.log.Timber
import javax.inject.Inject
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt
index 8910cc3976..6d386a17e5 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt
@@ -21,16 +21,27 @@ import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import io.element.android.features.networkmonitor.api.NetworkMonitor
+import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.MatrixClient
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter
import io.element.android.libraries.push.api.PushService
+import kotlinx.coroutines.delay
import javax.inject.Inject
+private const val DELAY_BEFORE_SHOWING_SYNC_SPINNER_IN_MILLIS = 1500L
+
class LoggedInPresenter @Inject constructor(
private val matrixClient: MatrixClient,
private val permissionsPresenterFactory: PermissionsPresenter.Factory,
+ private val networkMonitor: NetworkMonitor,
private val pushService: PushService,
) : Presenter {
@@ -53,18 +64,25 @@ class LoggedInPresenter @Inject constructor(
pushService.registerWith(matrixClient, pushProvider, distributor)
}
- val syncState = matrixClient.syncService().syncState.collectAsState()
+ val roomListState by matrixClient.roomListService.state.collectAsState()
+ val networkStatus by networkMonitor.connectivity.collectAsState()
val permissionsState = postNotificationPermissionsPresenter.present()
-
- // fun handleEvents(event: LoggedInEvents) {
- // when (event) {
- // }
- // }
-
+ var showSyncSpinner by remember {
+ mutableStateOf(false)
+ }
+ LaunchedEffect(roomListState, networkStatus) {
+ showSyncSpinner = when {
+ networkStatus == NetworkStatus.Offline -> false
+ roomListState == RoomListService.State.Running -> false
+ else -> {
+ delay(DELAY_BEFORE_SHOWING_SYNC_SPINNER_IN_MILLIS)
+ true
+ }
+ }
+ }
return LoggedInState(
- syncState = syncState.value,
+ showSyncSpinner = showSyncSpinner,
permissionsState = permissionsState,
- // eventSink = ::handleEvents
)
}
}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt
index 075242cddb..bb06952a50 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt
@@ -16,11 +16,9 @@
package io.element.android.appnav.loggedin
-import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.permissions.api.PermissionsState
data class LoggedInState(
- val syncState: SyncState,
+ val showSyncSpinner: Boolean,
val permissionsState: PermissionsState,
- // val eventSink: (LoggedInEvents) -> Unit
)
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt
index e8a8a4762c..3cfb03f123 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt
@@ -17,22 +17,20 @@
package io.element.android.appnav.loggedin
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.permissions.api.createDummyPostNotificationPermissionsState
open class LoggedInStateProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- aLoggedInState(),
- aLoggedInState(syncState = SyncState.Idle),
+ aLoggedInState(false),
+ aLoggedInState(true),
// Add other state here
)
}
fun aLoggedInState(
- syncState: SyncState = SyncState.Running,
+ showSyncSpinner: Boolean = true,
) = LoggedInState(
- syncState = syncState,
+ showSyncSpinner = showSyncSpinner,
permissionsState = createDummyPostNotificationPermissionsState(),
- // eventSink = {}
)
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt
index 60784ea4ed..0ade93a795 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt
@@ -47,7 +47,7 @@ fun LoggedInView(
modifier = Modifier
.padding(top = 8.dp)
.align(Alignment.TopCenter),
- syncState = state.syncState,
+ isVisible = state.showSyncSpinner,
)
PermissionsView(
state = state.permissionsState,
@@ -58,7 +58,7 @@ fun LoggedInView(
@DayNightPreviews
@Composable
-fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview {
+internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview {
LoggedInView(
state = state
)
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt
index 5108bb8716..6d045a431a 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt
@@ -38,19 +38,18 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
-import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun SyncStateView(
- syncState: SyncState,
+ isVisible: Boolean,
modifier: Modifier = Modifier
) {
val animationSpec = spring(stiffness = 500F)
AnimatedVisibility(
modifier = modifier,
- visible = syncState.mustBeVisible(),
+ visible = isVisible,
enter = fadeIn(animationSpec = animationSpec),
exit = fadeOut(animationSpec = animationSpec),
) {
@@ -60,15 +59,15 @@ fun SyncStateView(
) {
Row(
modifier = Modifier
- .background(color = ElementTheme.colors.bgSubtleSecondary)
- .padding(horizontal = 24.dp, vertical = 10.dp),
+ .background(color = ElementTheme.colors.bgSubtleSecondary)
+ .padding(horizontal = 24.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
CircularProgressIndicator(
modifier = Modifier
- .progressSemantics()
- .size(12.dp),
+ .progressSemantics()
+ .size(12.dp),
color = ElementTheme.colors.textPrimary,
strokeWidth = 1.5.dp,
)
@@ -82,20 +81,13 @@ fun SyncStateView(
}
}
-private fun SyncState.mustBeVisible() = when (this) {
- SyncState.Idle -> true /* Cold start of the app */
- SyncState.Running -> false
- SyncState.Error -> false /* In this case, the network error banner can be displayed */
- SyncState.Terminated -> true /* The app is resumed and the sync is started again */
-}
-
@DayNightPreviews
@Composable
-fun SyncStateViewPreview() = ElementPreview {
+internal fun SyncStateViewPreview() = ElementPreview {
// Add a box to see the shadow
Box(modifier = Modifier.padding(24.dp)) {
SyncStateView(
- syncState = SyncState.Idle
+ isVisible = true
)
}
}
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt
index e8d68a3e94..558f64424a 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt
@@ -16,19 +16,13 @@
package io.element.android.appnav.room
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -37,9 +31,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
-import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
+import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@@ -48,7 +41,6 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
-import io.element.android.libraries.designsystem.theme.placeholderBackground
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -103,20 +95,7 @@ private fun LoadingRoomTopBar(
BackButton(onClick = onBackClicked)
},
title = {
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
- Box(
- modifier = Modifier
- .size(AvatarSize.TimelineRoom.dp)
- .align(Alignment.CenterVertically)
- .background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape)
- )
- Spacer(modifier = Modifier.width(8.dp))
- PlaceholderAtom(width = 20.dp, height = 7.dp)
- Spacer(modifier = Modifier.width(7.dp))
- PlaceholderAtom(width = 45.dp, height = 7.dp)
- }
+ IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
},
windowInsets = WindowInsets(0.dp),
)
@@ -124,12 +103,12 @@ private fun LoadingRoomTopBar(
@Preview
@Composable
-fun LoadingRoomNodeViewLightPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) =
+internal fun LoadingRoomNodeViewLightPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun LoadingRoomNodeViewDarkPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) =
+internal fun LoadingRoomNodeViewDarkPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt
index 20ec9f48b4..661d3c5433 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt
@@ -96,7 +96,8 @@ class RoomFlowNode @AssistedInject constructor(
} else {
backstack.newRoot(NavTarget.Loading)
}
- }.launchIn(lifecycleScope)
+ }
+ .launchIn(lifecycleScope)
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt
index 73a8579b07..d00c4791f7 100644
--- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt
+++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt
@@ -20,6 +20,7 @@ import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe
@@ -74,8 +75,8 @@ class RoomLoadedFlowNode @AssistedInject constructor(
}
interface LifecycleCallback : NodeLifecycleCallback {
- fun onFlowCreated(identifier: String, room: MatrixRoom) = Unit
- fun onFlowReleased(identifier: String, room: MatrixRoom) = Unit
+ fun onFlowCreated(identifier: String, room: MatrixRoom)
+ fun onFlowReleased(identifier: String, room: MatrixRoom)
}
data class Inputs(
@@ -114,7 +115,8 @@ class RoomLoadedFlowNode @AssistedInject constructor(
room.updateMembers()
.onFailure {
Timber.e(it, "Fail to fetch members for room ${room.roomId}")
- }.onSuccess {
+ }
+ .onSuccess {
Timber.v("Success fetching members for room ${room.roomId}")
}
}
@@ -161,13 +163,16 @@ class RoomLoadedFlowNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
- // Rely on the View Lifecycle instead of the Node Lifecycle,
+ // Rely on the View Lifecycle in addition to the Node Lifecycle,
// because this node enters 'onDestroy' before his children, so it can leads to
// using the room in a child node where it's already closed.
DisposableEffect(Unit) {
- inputs.room.open()
+ inputs.room.subscribeToSync()
onDispose {
- inputs.room.close()
+ inputs.room.unsubscribeFromSync()
+ if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
+ inputs.room.destroy()
+ }
}
}
Children(
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
index 0efa9e7f3b..dad9365921 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.appnav
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@ class RootPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -54,7 +54,7 @@ class RootPresenterTest {
showError("Bad news", "Something bad happened")
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
index 83bda0ad82..4abc89e7ee 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
@@ -16,17 +16,22 @@
package io.element.android.appnav.loggedin
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
+import io.element.android.features.networkmonitor.api.NetworkStatus
+import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
import io.element.android.libraries.matrix.api.MatrixClient
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.test.FakeMatrixClient
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider
+import io.element.android.tests.testutils.consumeItemsUntilPredicate
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -34,7 +39,7 @@ class LoggedInPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -42,14 +47,33 @@ class LoggedInPresenterTest {
}
}
- private fun createPresenter(): LoggedInPresenter {
+ @Test
+ fun `present - show sync spinner`() = runTest {
+ val roomListService = FakeRoomListService()
+ val presenter = createPresenter(roomListService, NetworkStatus.Online)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ assertThat(initialState.showSyncSpinner).isFalse()
+ consumeItemsUntilPredicate { it.showSyncSpinner }
+ roomListService.postState(RoomListService.State.Running)
+ consumeItemsUntilPredicate { !it.showSyncSpinner }
+ }
+ }
+
+ private fun createPresenter(
+ roomListService: RoomListService = FakeRoomListService(),
+ networkStatus: NetworkStatus = NetworkStatus.Offline
+ ): LoggedInPresenter {
return LoggedInPresenter(
- matrixClient = FakeMatrixClient(),
+ matrixClient = FakeMatrixClient(roomListService = roomListService),
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
override fun create(permission: String): PermissionsPresenter {
return NoopPermissionsPresenter()
}
},
+ networkMonitor = FakeNetworkMonitor(networkStatus),
pushService = object : PushService {
override fun notificationStyleChanged() {
}
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt
index 17b6f6deb9..f56367e5f8 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt
@@ -18,12 +18,12 @@ package io.element.android.appnav.room
import app.cash.turbine.test
import com.google.common.truth.Truth
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
+import io.element.android.libraries.matrix.api.roomlist.RoomList
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
-import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -47,29 +47,29 @@ class LoadingRoomStateFlowFactoryTest {
@Test
fun `flow should emit Loading and then Loaded when there is a room in cache after SS is loaded`() = runTest {
val room = FakeMatrixRoom(sessionId= A_SESSION_ID, roomId = A_ROOM_ID)
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- val matrixClient = FakeMatrixClient(A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ val matrixClient = FakeMatrixClient(A_SESSION_ID, roomListService = roomListService)
val flowFactory = LoadingRoomStateFlowFactory(matrixClient)
flowFactory
.create(this, A_ROOM_ID)
.test {
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
matrixClient.givenGetRoomResult(A_ROOM_ID, room)
- roomSummaryDataSource.postLoadingState(RoomSummaryDataSource.LoadingState.Loaded(1))
+ roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room))
}
}
@Test
fun `flow should emit Loading and then Error when there is no room in cache after SS is loaded`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- val matrixClient = FakeMatrixClient(A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ val matrixClient = FakeMatrixClient(A_SESSION_ID, roomListService = roomListService)
val flowFactory = LoadingRoomStateFlowFactory(matrixClient)
flowFactory
.create(this, A_ROOM_ID)
.test {
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
- roomSummaryDataSource.postLoadingState(RoomSummaryDataSource.LoadingState.Loaded(1))
+ roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Error)
}
}
diff --git a/build.gradle.kts b/build.gradle.kts
index c03881144e..a50b7673e4 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -56,7 +56,7 @@ allprojects {
// activate all available (even unstable) rules.
allRules = true
// point to your custom config defining rules to run, overwriting default behavior
- config = files("$rootDir/tools/detekt/detekt.yml")
+ config.from(files("$rootDir/tools/detekt/detekt.yml"))
}
dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.1.12")
@@ -90,6 +90,14 @@ allprojects {
apply {
plugin("org.owasp.dependencycheck")
}
+
+ tasks.withType {
+ // Warnings are potential errors, so stop ignoring them
+ // This is disabled by default, but the CI will enforce this.
+ // You can override by passing `-PallWarningsAsErrors=true` in the command line
+ // Or add a line with "allWarningsAsErrors=true" in your ~/.gradle/gradle.properties file
+ kotlinOptions.allWarningsAsErrors = project.properties["allWarningsAsErrors"] == "true"
+ }
}
// To run a sonar analysis:
diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md
index 9198137577..8a587b5a08 100644
--- a/docs/_developer_onboarding.md
+++ b/docs/_developer_onboarding.md
@@ -145,7 +145,7 @@ Then you can launch the build script from the matrix-rust-components-kotlin repo
- `-m` Option to select the gradle module to build. Default is sdk.
- `-t` Option to to select an android target to build against. Default will build for all targets.
-So for example to build the sdk against aarch64-linux-android target and copy the generated aar to ElementX project:
+So for example to build the sdk against aarch64-linux-android target and copy the generated aar to Element X project:
```shell
./scripts/build.sh -p [YOUR MATRIX RUST SDK PATH] -t aarch64-linux-android -o [YOUR element-x-android PATH]/libraries/rustsdk/matrix-rust-sdk.aar
@@ -313,7 +313,7 @@ suffix `Presenter`,states MUST have a suffix `State`, etc. Also we want to have
### Push
-**Note** Firebase Push is not yet implemented on the project.
+**Note** Firebase is implemented, but Unified Push is not yet fully implemented on the project, so this is not possible to choose this push provider in the app at the moment.
Please see the dedicated [documentation](notifications.md) for more details.
@@ -342,8 +342,7 @@ We have 3 tests frameworks in place, and this should be sufficient to guarantee
file [TemplateView.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateView.kt). We create PreviewProvider to provide
different states. See for instance the
file [TemplateStateProvider.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateStateProvider.kt)
- - Tests on presenter with [Molecule](https://github.com/cashapp/molecule) and [Turbine](https://github.com/cashapp/turbine). See in the template the
- class [TemplatePresenterTests](../features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt).
+- Tests on presenter with [Molecule](https://github.com/cashapp/molecule) and [Turbine](https://github.com/cashapp/turbine). See in the template the class [TemplatePresenterTests](../features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt).
**Note** For now we want to avoid using class mocking (with library such as *mockk*), because this should be not necessary. We prefer to create Fake
implementation of our interfaces. Mocking can be used to mock Android framework classes though, such as `Bitmap` for instance.
diff --git a/docs/images-lfs/screen_1_dark.png b/docs/images-lfs/screen_1_dark.png
new file mode 100644
index 0000000000..8bdcd59305
--- /dev/null
+++ b/docs/images-lfs/screen_1_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4515f7589c422197a82672cdc3e64814ccca9a9a022b806facee44dd67d51ff2
+size 1116864
diff --git a/docs/images-lfs/screen_1_light.png b/docs/images-lfs/screen_1_light.png
new file mode 100644
index 0000000000..8eba38af82
--- /dev/null
+++ b/docs/images-lfs/screen_1_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2afb667a8b679f4395c28407b014f074e8b74745f6ecdd6ad699a6650cee2bc7
+size 771160
diff --git a/docs/images-lfs/screen_2_dark.png b/docs/images-lfs/screen_2_dark.png
new file mode 100644
index 0000000000..9a0102940f
--- /dev/null
+++ b/docs/images-lfs/screen_2_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:910f9ab58a197a16b1295cd6b65d406ebfff1298c9c128a326edf1ec834d7fa7
+size 332936
diff --git a/docs/images-lfs/screen_2_light.png b/docs/images-lfs/screen_2_light.png
new file mode 100644
index 0000000000..1dd3106e5c
--- /dev/null
+++ b/docs/images-lfs/screen_2_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f7b80b9124c5c04c9db3824b68ab35883d41d05918abd415f7565f87d2713cfa
+size 338455
diff --git a/docs/images-lfs/screen_3_dark.png b/docs/images-lfs/screen_3_dark.png
new file mode 100644
index 0000000000..ccc17333e7
--- /dev/null
+++ b/docs/images-lfs/screen_3_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dadff79ae955ab1da5c0a0567960fc567526ad0d8d2ea418d8adacf3a7a400cc
+size 243201
diff --git a/docs/images-lfs/screen_3_light.png b/docs/images-lfs/screen_3_light.png
new file mode 100644
index 0000000000..2116f1dc4c
--- /dev/null
+++ b/docs/images-lfs/screen_3_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e2f328b3e8bf2ebe4abc4c130e28f6fc2351f5ac339be0819e5fddb657f4a560
+size 246731
diff --git a/docs/images-lfs/screen_4_dark.png b/docs/images-lfs/screen_4_dark.png
new file mode 100644
index 0000000000..5bd122a9ea
--- /dev/null
+++ b/docs/images-lfs/screen_4_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:519fb54f0070833b2a4671e1f0fc7b2ec60930d69b592b8a760f52a73dc4fe38
+size 132247
diff --git a/docs/images-lfs/screen_4_light.png b/docs/images-lfs/screen_4_light.png
new file mode 100644
index 0000000000..ee82f3be0a
--- /dev/null
+++ b/docs/images-lfs/screen_4_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f683a228d7168c75d6f42c353c1a0e169cb22cb8fa6696e9e3ffeb80854b2441
+size 131610
diff --git a/docs/images/screen1.png b/docs/images/screen1.png
deleted file mode 100644
index 9f9d7747ff..0000000000
Binary files a/docs/images/screen1.png and /dev/null differ
diff --git a/docs/images/screen2.png b/docs/images/screen2.png
deleted file mode 100644
index a5733003d6..0000000000
Binary files a/docs/images/screen2.png and /dev/null differ
diff --git a/docs/images/screen3.png b/docs/images/screen3.png
deleted file mode 100644
index 3edb49d086..0000000000
Binary files a/docs/images/screen3.png and /dev/null differ
diff --git a/docs/images/screen4.png b/docs/images/screen4.png
deleted file mode 100644
index 53da801a1b..0000000000
Binary files a/docs/images/screen4.png and /dev/null differ
diff --git a/docs/maps.md b/docs/maps.md
index cc00905986..789d455f22 100644
--- a/docs/maps.md
+++ b/docs/maps.md
@@ -27,16 +27,21 @@ Place your API key in `local.properties` with the key
services.maptiler.apikey=abCd3fGhijK1mN0pQr5t
```
+Optionally you can also place your custom MapTyler style ids for light and dark maps
+in the `local.properties` with the keys `services.maptiler.lightMapId` and
+`services.maptiler.darkMapId`. If you don't specify these, the default MapTiler "basic-v2"
+styles will be used.
+
## Making releasable builds with MapTiler
To insert the MapTiler API key when building an APK, set the
`ELEMENT_ANDROID_MAPTILER_API_KEY` environment variable in your build
-environment.
+environment.
+If you've added custom styles also set the `ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID`
+and `ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID` environment variables accordingly.
## Using other map sources or MapTiler styles
-If you wish to use an alternative map provider, or custom MapTiler styles,
-you can customise the functions in
-`features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt`.
-We've kept this file small and self contained to minimise the chances of merge
-collisions in forks.
+If you wish to use an alternative map provider, you can provide your own implementations of
+`TileServerStyleUriBuilder` and `StaticMapUrlBuilder` in
+`features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/`.
diff --git a/docs/nightly_build.md b/docs/nightly_build.md
index 9abd59a67b..91ea10b530 100644
--- a/docs/nightly_build.md
+++ b/docs/nightly_build.md
@@ -10,11 +10,11 @@
## Configuration
-The nightly build will contain what's on develop, in release mode, for the main variant. It is signed using a dedicated signature, and has a dedicated appId (`io.element.android.x.nightly`), so it can be installed along with the production version of ElementX Android. The only other difference compared to ElementX Android is a different app name. We do not want to change the app name since it will also affect some strings in the app, and we do want to do that. (TODO today, the app name is changed.)
+The nightly build will contain what's on develop, in release mode, for the main variant. It is signed using a dedicated signature, and has a dedicated appId (`io.element.android.x.nightly`), so it can be installed along with the production version of Element X Android. The only other difference compared to ElementX Android is a different app name. We do not want to change the app name since it will also affect some strings in the app, and we do want to do that. (TODO today, the app name is changed.)
Nightly builds are built and released to Firebase every days, and automatically.
-This is recommended to exclusively use this app, with your main account, instead of ElementX Android, and fallback to ElementX Android just in case of regression, to discover as soon as possible any regression, and report it to the team. To avoid double notification, you may want to disable the notification from the Element Android production version. Just open Element Android, navigate to `Settings/Notifications` and uncheck `Enable notifications for this session` (TODO Not supported yet).
+This is recommended to exclusively use this app, with your main account, instead of Element X Android, and fallback to ElementX Android just in case of regression, to discover as soon as possible any regression, and report it to the team. To avoid double notification, you may want to disable the notification from the Element Android production version. Just open Element Android, navigate to `Settings/Notifications` and uncheck `Enable notifications for this session` (TODO Not supported yet).
*Note:* Due to a limitation of Firebase, the nightly build is the universal build, which means that the size of the APK is a bit bigger, but this should not have any other side effect.
diff --git a/docs/screenshot_testing.md b/docs/screenshot_testing.md
index 37299af7fc..79ecad20dd 100644
--- a/docs/screenshot_testing.md
+++ b/docs/screenshot_testing.md
@@ -13,7 +13,7 @@
## Overview
- Screenshot tests are tests which record the content of a rendered screen and verify subsequent runs to check if the screen renders differently.
-- ElementX uses [Paparazzi](https://github.com/cashapp/paparazzi) to render, record and verify Composable. All Composable Preview will be use to make screenshot test, thanks to the usage of [Showkase](https://github.com/airbnb/Showkase).
+- Element X uses [Paparazzi](https://github.com/cashapp/paparazzi) to render, record and verify Composable. All Composable Preview will be use to make screenshot test, thanks to the usage of [Showkase](https://github.com/airbnb/Showkase).
- The screenshot verification occurs on every pull request as part of the `tests.yml` workflow.
## Setup
@@ -30,6 +30,14 @@ If installed correctly, `git push` and `git pull` will now include LFS content.
## Recording
+Recording of screenshots is done by triggering the GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml), to avoid differences of generated binary files (png images) depending on developers' environment.
+
+So basically, you will create a branch, do some commits with your work on it, then push your branch, trigger the GitHub action to record the screenshots (only if you think preview may have changed), and finally create a pull request. The GitHub action will record the screenshots and commit the changes to the branch.
+
+You can still record the screenshots locally, but please do not commit the changes.
+
+To record the screenshot locally, run the following command:
+
```shell
./gradlew recordPaparazziDebug
```
diff --git a/fastlane/metadata/android/en-US/changelogs/40001020.txt b/fastlane/metadata/android/en-US/changelogs/40001020.txt
new file mode 100644
index 0000000000..8aacdd89af
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40001020.txt
@@ -0,0 +1,2 @@
+First release of Element X 🚀!
+Full changelog: https://github.com/vector-im/element-x-android/releases
diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt
index f6d77226b9..5aa9287d86 100644
--- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt
+++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt
@@ -81,12 +81,12 @@ fun buildAnnotatedStringWithColoredPart(
@Preview
@Composable
-fun AnalyticsPreferencesViewLightPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) =
+internal fun AnalyticsPreferencesViewLightPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun AnalyticsPreferencesViewDarkPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) =
+internal fun AnalyticsPreferencesViewDarkPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/analytics/impl/build.gradle.kts b/features/analytics/impl/build.gradle.kts
index 3bf58ab636..8356bb38bb 100644
--- a/features/analytics/impl/build.gradle.kts
+++ b/features/analytics/impl/build.gradle.kts
@@ -52,6 +52,4 @@ dependencies {
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.features.analytics.test)
testImplementation(projects.features.analytics.impl)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
index a27e6e7399..54b9add785 100644
--- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
+++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt
@@ -54,6 +54,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart
import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
@@ -188,29 +189,28 @@ private fun AnalyticsOptInFooter(
modifier = modifier,
) {
Button(
+ text = stringResource(id = CommonStrings.action_ok),
onClick = onTermsAccepted,
modifier = Modifier.fillMaxWidth(),
- ) {
- Text(text = stringResource(id = CommonStrings.action_ok))
- }
+ )
TextButton(
+ text = stringResource(id = CommonStrings.action_not_now),
+ size = ButtonSize.Medium,
onClick = onTermsDeclined,
modifier = Modifier.fillMaxWidth(),
- ) {
- Text(text = stringResource(id = CommonStrings.action_not_now))
- }
+ )
}
}
@Preview
@Composable
-fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight {
+internal fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight {
ContentToPreview(state)
}
@Preview
@Composable
-fun AnalyticsOptInViewDarkPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewDark {
+internal fun AnalyticsOptInViewDarkPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewDark {
ContentToPreview(state)
}
diff --git a/features/analytics/impl/src/main/res/values-de/translations.xml b/features/analytics/impl/src/main/res/values-de/translations.xml
index 979048344f..44890927a4 100644
--- a/features/analytics/impl/src/main/res/values-de/translations.xml
+++ b/features/analytics/impl/src/main/res/values-de/translations.xml
@@ -1,10 +1,10 @@
- "Wir erfassen und analysieren ""keine"" Account-Daten"
+ "Wir werden keine personenbezogenen Daten aufzeichnen oder auswerten"
"Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."
- "Sie können alle unsere Nutzerbedingungen %1$s lesen."
+ "Du kannst alle unsere Nutzerbedingungen %1$s lesen."
"hier"
- "Sie können die Analyse jederzeit in den Einstellungen deaktivieren"
+ "Du kannst dies jederzeit deaktivieren"
"Wir geben ""keine"" Informationen an Dritte weiter"
- "Helfen Sie %1$s zu verbessern"
+ "Hilf uns, %1$s zu verbessern"
diff --git a/features/analytics/impl/src/main/res/values-ru/translations.xml b/features/analytics/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..805cba6bf2
--- /dev/null
+++ b/features/analytics/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,10 @@
+
+
+ "Мы не будем записывать или профилировать какие-либо персональные данные"
+ "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы."
+ "Вы можете ознакомиться со всеми нашими условиями %1$s."
+ "здесь"
+ "Вы можете отключить эту функцию в любое время"
+ "Мы не будем передавать ваши данные третьим лицам"
+ "Помогите улучшить %1$s"
+
diff --git a/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..3259b10fbd
--- /dev/null
+++ b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,4 @@
+
+
+ "您可以在任何時候關閉它"
+
diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
index a8e42ceb01..541e5858bd 100644
--- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
+++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.analytics.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -35,7 +35,7 @@ class AnalyticsOptInPresenterTest {
aBuildMeta(),
analyticsService
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -53,7 +53,7 @@ class AnalyticsOptInPresenterTest {
aBuildMeta(),
analyticsService
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
index 8469abe769..d0914932bb 100644
--- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
+++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.analytics.impl.preferences
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -33,7 +33,7 @@ class AnalyticsPreferencesPresenterTest {
FakeAnalyticsService(isEnabled = true, didAskUserConsent = true),
aBuildMeta()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -48,7 +48,7 @@ class AnalyticsPreferencesPresenterTest {
FakeAnalyticsService(isEnabled = false, didAskUserConsent = false),
aBuildMeta()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -62,7 +62,7 @@ class AnalyticsPreferencesPresenterTest {
FakeAnalyticsService(isEnabled = true, didAskUserConsent = true),
aBuildMeta()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt b/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt
index 6e84c58d2a..9e3fe8bb59 100644
--- a/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt
+++ b/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt
@@ -33,7 +33,7 @@ class FakeAnalyticsService(
private val didAskUserConsentFlow = MutableStateFlow(didAskUserConsent)
val capturedEvents = mutableListOf()
- override fun getAvailableAnalyticsProviders(): List = emptyList()
+ override fun getAvailableAnalyticsProviders(): Set = emptySet()
override fun getUserConsent(): Flow = isEnabledFlow
diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts
index 8bb343c10a..fe2970dd80 100644
--- a/features/createroom/impl/build.gradle.kts
+++ b/features/createroom/impl/build.gradle.kts
@@ -66,7 +66,5 @@ dependencies {
testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.libraries.usersearch.test)
- androidTestImplementation(libs.test.junitext)
-
ksp(libs.showkase.processor)
}
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt
index da1f43391b..e5f701036e 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt
@@ -28,7 +28,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.unit.dp
import io.element.android.features.createroom.impl.R
import io.element.android.features.createroom.impl.components.UserListView
import io.element.android.features.createroom.impl.userlist.UserListEvents
@@ -36,7 +35,6 @@ import io.element.android.features.createroom.impl.userlist.UserListState
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
@@ -103,16 +101,11 @@ fun AddPeopleViewTopBar(
},
navigationIcon = { BackButton(onClick = onBackPressed) },
actions = {
+ val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip
TextButton(
- modifier = Modifier.padding(horizontal = 8.dp),
+ text = stringResource(id = textActionResId),
onClick = onNextPressed,
- ) {
- val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip
- Text(
- text = stringResource(id = textActionResId),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
}
)
}
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt
index ee664673f8..302f72d514 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt
@@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import io.element.android.features.createroom.impl.configureroom.RoomPrivacyItem
import io.element.android.features.createroom.impl.configureroom.roomPrivacyItems
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@@ -94,11 +93,11 @@ fun RoomPrivacyOption(
@Preview
@Composable
-fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() }
+internal fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
-fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() }
+internal fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
index 9ac8f0fbde..d1f29559bd 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
@@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetValue
@@ -43,6 +44,7 @@ import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -55,7 +57,6 @@ import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
@@ -193,15 +194,10 @@ fun ConfigureRoomToolbar(
navigationIcon = { BackButton(onClick = onBackPressed) },
actions = {
TextButton(
- modifier = Modifier.padding(horizontal = 8.dp),
+ text = stringResource(CommonStrings.action_create),
enabled = isNextActionEnabled,
onClick = onNextPressed,
- ) {
- Text(
- text = stringResource(CommonStrings.action_create),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
}
)
}
@@ -247,6 +243,9 @@ fun RoomTopic(
placeholder = stringResource(CommonStrings.common_topic_placeholder),
onValueChange = onTopicChanged,
maxLines = 3,
+ keyboardOptions = KeyboardOptions(
+ capitalization = KeyboardCapitalization.Sentences,
+ ),
)
}
@@ -277,12 +276,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
@Preview
@Composable
-fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
+internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
+internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt
index d1484b7a4f..7a3bf6ef03 100644
--- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt
+++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt
@@ -41,7 +41,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider"Inviter des amis sur Element"
"Inviter des personnes"
"Une erreur s\'est produite lors de la création du salon"
- "Les messages dans ce salon sont chiffrés. Une fopis activé, le chiffrement ne peut pas être désactivé."
+ "Les messages dans ce salon sont chiffrés. Une fois activé, le chiffrement ne peut pas être désactivé."
"Salon privé (sur invitation uniquement)"
"Les messages ne sont pas chiffrés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."
"Salon public (n’importe qui)"
diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..7837f9e7de
--- /dev/null
+++ b/features/createroom/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,15 @@
+
+
+ "Новая комната"
+ "Пригласите друзей в Element"
+ "Пригласить людей"
+ "Произошла ошибка при создании комнаты"
+ "Сообщения в этой комнате зашифрованы. Отключить шифрование впоследствии невозможно."
+ "Приватная комната (только по приглашению)"
+ "Сообщения не зашифрованы, и каждый может их прочитать. Вы можете включить шифрование позже."
+ "Публичная комната (любой)"
+ "Название комнаты"
+ "Тема (необязательно)"
+ "Произошла ошибка при попытке открытия комнаты"
+ "Создать комнату"
+
diff --git a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..dd8afaf2e7
--- /dev/null
+++ b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "邀請朋友使用 Element"
+ "聊天室名稱"
+ "主題(非必填)"
+ "建立聊天室"
+
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
index fe8c7b7462..43cf6cff4d 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.createroom.impl.addpeople
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -43,7 +43,7 @@ class AddPeoplePresenterTests {
@Test
fun `present - initial state`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// TODO This doesn't actually test anything...
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
index 9b6ac2e067..1703cf4142 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
@@ -17,7 +17,7 @@
package io.element.android.features.createroom.impl.configureroom
import android.net.Uri
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -93,7 +93,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - initial state`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -108,7 +108,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - create room button is enabled only if the required fields are completed`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -133,7 +133,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - state is updated when fields are changed`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -203,7 +203,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - trigger create room action`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -221,7 +221,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - record analytics when creating room`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -240,7 +240,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - trigger create room with upload error and retry`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -265,7 +265,7 @@ class ConfigureRoomPresenterTests {
@Test
fun `present - trigger retry and cancel actions`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
index 8976c77b8e..002e8c77fd 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.createroom.impl.root
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -27,8 +27,6 @@ import io.element.android.features.createroom.impl.userlist.FakeUserListPresente
import io.element.android.features.createroom.impl.userlist.UserListDataStore
import io.element.android.features.createroom.impl.userlist.aUserListState
import io.element.android.libraries.architecture.Async
-import io.element.android.libraries.core.meta.BuildMeta
-import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -68,7 +66,7 @@ class CreateRoomRootPresenterTests {
@Test
fun `present - initial state`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -82,7 +80,7 @@ class CreateRoomRootPresenterTests {
@Test
fun `present - trigger create DM action`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -102,7 +100,7 @@ class CreateRoomRootPresenterTests {
@Test
fun `present - creating a DM records analytics event`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -123,7 +121,7 @@ class CreateRoomRootPresenterTests {
@Test
fun `present - trigger retrieve DM action`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -142,7 +140,7 @@ class CreateRoomRootPresenterTests {
@Test
fun `present - trigger retry create DM action`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
index 745bdf74f9..3561387dcf 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.createroom.impl.userlist
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -41,7 +41,7 @@ class DefaultUserListPresenterTests {
userRepository,
UserListDataStore(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -62,7 +62,7 @@ class DefaultUserListPresenterTests {
userRepository,
UserListDataStore(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -83,7 +83,7 @@ class DefaultUserListPresenterTests {
userRepository,
UserListDataStore(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -119,7 +119,7 @@ class DefaultUserListPresenterTests {
userRepository,
UserListDataStore(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -158,7 +158,7 @@ class DefaultUserListPresenterTests {
userRepository,
UserListDataStore(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -183,7 +183,7 @@ class DefaultUserListPresenterTests {
userRepository,
UserListDataStore(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt
index 7397e5ecc5..eebef5b3f2 100644
--- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt
+++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt
@@ -97,9 +97,11 @@ fun WelcomeView(
}
},
footer = {
- Button(modifier = Modifier.fillMaxWidth(), onClick = onContinueClicked) {
- Text(text = stringResource(CommonStrings.action_continue))
- }
+ Button(
+ text = stringResource(CommonStrings.action_continue),
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onContinueClicked
+ )
Spacer(modifier = Modifier.height(32.dp))
}
)
diff --git a/features/ftue/impl/src/main/res/values-de/translations.xml b/features/ftue/impl/src/main/res/values-de/translations.xml
new file mode 100644
index 0000000000..0f2efc1c57
--- /dev/null
+++ b/features/ftue/impl/src/main/res/values-de/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "Anrufe, Standortfreigabe, Suche und mehr werden später in diesem Jahr hinzugefügt."
+ "Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein."
+ "Wir würden uns freuen, wenn du uns über die Einstellungsseite deine Meinung mitteilst."
+ "Los geht\'s!"
+ "Folgendes musst du wissen:"
+ "Willkommen bei %1$s!"
+
diff --git a/features/ftue/impl/src/main/res/values-fr/translations.xml b/features/ftue/impl/src/main/res/values-fr/translations.xml
new file mode 100644
index 0000000000..313a6a533f
--- /dev/null
+++ b/features/ftue/impl/src/main/res/values-fr/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour."
+ "Nous serions ravis d’avoir votre avis, n’hésitez pas à nous le partager via la page des paramètres."
+ "C’est parti !"
+ "Voici ce qu’il faut savoir :"
+ "Bienvenue sur %1$s !"
+
diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..db4fcd21fc
--- /dev/null
+++ b/features/ftue/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."
+ "История сообщений для зашифрованных комнат в этом обновлении будет недоступна."
+ "Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек."
+ "Поехали!"
+ "Вот что вам необходимо знать:"
+ "Добро пожаловать в %1$s!"
+
diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml
new file mode 100644
index 0000000000..2438518334
--- /dev/null
+++ b/features/ftue/impl/src/main/res/values-sk/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku."
+ "História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii."
+ "Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení."
+ "Poďme na to!"
+ "Tu je to, čo potrebujete vedieť:"
+ "Vitajte v %1$s!"
+
diff --git a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..6c5d482cb8
--- /dev/null
+++ b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,4 @@
+
+
+ "開始吧!"
+
diff --git a/features/ftue/impl/src/main/res/values/localazy.xml b/features/ftue/impl/src/main/res/values/localazy.xml
index 17999e7158..05cc72034e 100644
--- a/features/ftue/impl/src/main/res/values/localazy.xml
+++ b/features/ftue/impl/src/main/res/values/localazy.xml
@@ -1,6 +1,6 @@
- "Calls, location sharing, search and more will be added later this year."
+ "Calls, polls, search and more will be added later this year."
"Message history for encrypted rooms won’t be available in this update."
"We’d love to hear from you, let us know what you think via the settings page."
"Let\'s go!"
diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt
index ce1683e8e5..cfd489ea2a 100644
--- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt
+++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt
@@ -25,7 +25,6 @@ import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt
index 21a57b48a7..8c43b815ae 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt
@@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummary
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.extensions.toAnalyticsJoinedRoom
@@ -56,8 +56,9 @@ class InviteListPresenter @Inject constructor(
@Composable
override fun present(): InviteListState {
val invites by client
- .roomSummaryDataSource
- .inviteRooms()
+ .roomListService
+ .invites()
+ .summaries
.collectAsState()
var seenInvites by remember { mutableStateOf>(emptySet()) }
@@ -152,8 +153,7 @@ class InviteListPresenter @Inject constructor(
client.getRoom(roomId)?.use {
it.leave().getOrThrow()
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
- }
- Unit
+ }.let { }
}.runCatchingUpdatingState(declinedAction)
}
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
index e2e5927a66..a129668305 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt
@@ -86,7 +86,6 @@ fun InviteListView(
title = stringResource(titleResource),
submitText = stringResource(CommonStrings.action_decline),
cancelText = stringResource(CommonStrings.action_cancel),
- emphasizeSubmitButton = true,
onSubmitClicked = { state.eventSink(InviteListEvents.ConfirmDeclineInvite) },
onDismiss = { state.eventSink(InviteListEvents.CancelDeclineInvite) }
)
diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
index c2bbd5b023..c677fe158f 100644
--- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
+++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt
@@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -49,8 +48,8 @@ import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAto
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@@ -133,23 +132,19 @@ internal fun DefaultInviteSummaryRow(
// CTAs
Row(Modifier.padding(top = 12.dp)) {
OutlinedButton(
- content = { Text(stringResource(CommonStrings.action_decline), style = ElementTheme.typography.aliasButtonText) },
+ text = stringResource(CommonStrings.action_decline),
onClick = onDeclineClicked,
- modifier = Modifier
- .weight(1f)
- .heightIn(max = 36.dp),
- contentPadding = PaddingValues(horizontal = 24.dp, vertical = 0.dp),
+ modifier = Modifier.weight(1f),
+ size = ButtonSize.Medium,
)
Spacer(modifier = Modifier.width(12.dp))
Button(
- content = { Text(stringResource(CommonStrings.action_accept), style = ElementTheme.typography.aliasButtonText) },
+ text = stringResource(CommonStrings.action_accept),
onClick = onAcceptClicked,
- modifier = Modifier
- .weight(1f)
- .heightIn(max = 36.dp),
- contentPadding = PaddingValues(horizontal = 24.dp, vertical = 0.dp),
+ modifier = Modifier.weight(1f),
+ size = ButtonSize.Medium,
)
}
}
diff --git a/features/invitelist/impl/src/main/res/values-de/translations.xml b/features/invitelist/impl/src/main/res/values-de/translations.xml
index 1e2fcc2e86..2cec59d6a0 100644
--- a/features/invitelist/impl/src/main/res/values-de/translations.xml
+++ b/features/invitelist/impl/src/main/res/values-de/translations.xml
@@ -1,8 +1,8 @@
- "Möchten Sie den Beitritt zu %1$s wirklich ablehnen?"
+ "Möchtest du den Beitritt zu %1$s wirklich ablehnen?"
"Einladung ablehnen"
- "Möchten Sie den Chat mit %1$s wirklich ablehnen?"
+ "Möchtest du den privaten Chat mit %1$s wirklich ablehnen?"
"Chat ablehnen"
"Keine Einladungen"
"%1$s (%2$s) hat dich eingeladen"
diff --git a/features/invitelist/impl/src/main/res/values-ru/translations.xml b/features/invitelist/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..0f1c30cb09
--- /dev/null
+++ b/features/invitelist/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "Вы уверены, что хотите отклонить приглашение в %1$s?"
+ "Отклонить приглашение"
+ "Вы уверены, что хотите отказаться от приватного общения с %1$s?"
+ "Отклонить чат"
+ "Нет приглашений"
+ "%1$s (%2$s) пригласил вас"
+
diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
index 1dd9068a1f..f3eef2784c 100644
--- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
+++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.invitelist.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -30,8 +30,8 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomMembershipState
-import io.element.android.libraries.matrix.api.room.RoomSummary
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
@@ -40,7 +40,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
-import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
import io.element.android.libraries.push.test.notifications.FakeNotificationDrawerManager
import io.element.android.services.analytics.api.AnalyticsService
@@ -51,17 +51,17 @@ class InviteListPresenterTests {
@Test
fun `present - starts empty, adds invites when received`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val presenter = createPresenter(
- FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ FakeMatrixClient(roomListService = roomListService)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.inviteList).isEmpty()
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary()))
+ roomListService.postInviteRooms(listOf(aRoomSummary()))
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
@@ -72,11 +72,11 @@ class InviteListPresenterTests {
@Test
fun `present - uses user ID and avatar for direct invites`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withDirectChatInvitation()
+ val roomListService = FakeRoomListService().withDirectChatInvitation()
val presenter = createPresenter(
- FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ FakeMatrixClient(roomListService = roomListService)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val withInviteState = awaitItem()
@@ -98,11 +98,11 @@ class InviteListPresenterTests {
@Test
fun `present - includes sender details for room invites`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val presenter = createPresenter(
- FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ FakeMatrixClient(roomListService = roomListService)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val withInviteState = awaitItem()
@@ -122,16 +122,16 @@ class InviteListPresenterTests {
@Test
fun `present - shows confirm dialog for declining direct chat invites`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withDirectChatInvitation()
+ val roomListService = FakeRoomListService().withDirectChatInvitation()
val presenter = InviteListPresenter(
FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
),
FakeSeenInvitesStore(),
FakeAnalyticsService(),
FakeNotificationDrawerManager()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -148,11 +148,11 @@ class InviteListPresenterTests {
@Test
fun `present - shows confirm dialog for declining room invites`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val presenter = createPresenter(
- FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ FakeMatrixClient(roomListService = roomListService)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -169,11 +169,11 @@ class InviteListPresenterTests {
@Test
fun `present - hides confirm dialog when cancelling`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val presenter = createPresenter(
- FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ FakeMatrixClient(roomListService = roomListService)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -190,16 +190,16 @@ class InviteListPresenterTests {
@Test
fun `present - declines invite after confirming`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val fakeNotificationDrawerManager = FakeNotificationDrawerManager()
val client = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
)
val room = FakeMatrixRoom()
val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager)
client.givenGetRoomResult(A_ROOM_ID, room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -217,9 +217,9 @@ class InviteListPresenterTests {
@Test
fun `present - declines invite after confirming and sets state on error`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val client = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
)
val room = FakeMatrixRoom()
val presenter = createPresenter(client)
@@ -227,7 +227,7 @@ class InviteListPresenterTests {
room.givenLeaveRoomError(ex)
client.givenGetRoomResult(A_ROOM_ID, room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -247,9 +247,9 @@ class InviteListPresenterTests {
@Test
fun `present - dismisses declining error state`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val client = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
)
val room = FakeMatrixRoom()
val presenter = createPresenter(client)
@@ -257,7 +257,7 @@ class InviteListPresenterTests {
room.givenLeaveRoomError(ex)
client.givenGetRoomResult(A_ROOM_ID, room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -279,16 +279,16 @@ class InviteListPresenterTests {
@Test
fun `present - accepts invites and sets state on success`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val fakeNotificationDrawerManager = FakeNotificationDrawerManager()
val client = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
)
val room = FakeMatrixRoom()
val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager)
client.givenGetRoomResult(A_ROOM_ID, room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -303,9 +303,9 @@ class InviteListPresenterTests {
@Test
fun `present - accepts invites and sets state on error`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val client = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
)
val room = FakeMatrixRoom()
val presenter = createPresenter(client)
@@ -313,7 +313,7 @@ class InviteListPresenterTests {
room.givenJoinRoomResult(Result.failure(ex))
client.givenGetRoomResult(A_ROOM_ID, room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -325,9 +325,9 @@ class InviteListPresenterTests {
@Test
fun `present - dismisses accepting error state`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation()
+ val roomListService = FakeRoomListService().withRoomInvitation()
val client = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
)
val room = FakeMatrixRoom()
val presenter = createPresenter(client)
@@ -335,7 +335,7 @@ class InviteListPresenterTests {
room.givenJoinRoomResult(Result.failure(ex))
client.givenGetRoomResult(A_ROOM_ID, room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val originalState = awaitItem()
@@ -352,35 +352,35 @@ class InviteListPresenterTests {
@Test
fun `present - stores seen invites when received`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val store = FakeSeenInvitesStore()
val presenter = InviteListPresenter(
FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
),
store,
FakeAnalyticsService(),
FakeNotificationDrawerManager()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitItem()
// When one invite is received, that ID is saved
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary()))
+ roomListService.postInviteRooms(listOf(aRoomSummary()))
awaitItem()
Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID))
// When a second is added, both are saved
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2)))
+ roomListService.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2)))
awaitItem()
Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID, A_ROOM_ID_2))
// When they're both dismissed, an empty set is saved
- roomSummaryDataSource.postInviteRooms(listOf())
+ roomListService.postInviteRooms(listOf())
awaitItem()
Truth.assertThat(store.getProvidedRoomIds()).isEmpty()
@@ -389,23 +389,23 @@ class InviteListPresenterTests {
@Test
fun `present - marks invite as new if they're unseen`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val store = FakeSeenInvitesStore()
store.publishRoomIds(setOf(A_ROOM_ID))
val presenter = InviteListPresenter(
FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource,
+ roomListService = roomListService,
),
store,
FakeAnalyticsService(),
FakeNotificationDrawerManager()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitItem()
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2)))
+ roomListService.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2)))
skipItems(1)
val withInviteState = awaitItem()
@@ -417,7 +417,7 @@ class InviteListPresenterTests {
}
}
- private suspend fun FakeRoomSummaryDataSource.withRoomInvitation(): FakeRoomSummaryDataSource {
+ private suspend fun FakeRoomListService.withRoomInvitation(): FakeRoomListService {
postInviteRooms(
listOf(
RoomSummary.Filled(
@@ -446,7 +446,7 @@ class InviteListPresenterTests {
return this
}
- private suspend fun FakeRoomSummaryDataSource.withDirectChatInvitation(): FakeRoomSummaryDataSource {
+ private suspend fun FakeRoomListService.withDirectChatInvitation(): FakeRoomListService {
postInviteRooms(
listOf(
RoomSummary.Filled(
diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
index 03eeb3adc6..3075d3c686 100644
--- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
+++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.leaveroom.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -40,7 +40,7 @@ class LeaveRoomPresenterImplTest {
@Test
fun `present - initial state hides all dialogs`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -60,7 +60,7 @@ class LeaveRoomPresenterImplTest {
)
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -80,7 +80,7 @@ class LeaveRoomPresenterImplTest {
)
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -100,7 +100,7 @@ class LeaveRoomPresenterImplTest {
)
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -122,7 +122,7 @@ class LeaveRoomPresenterImplTest {
},
roomMembershipObserver = roomMembershipObserver
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -145,7 +145,7 @@ class LeaveRoomPresenterImplTest {
)
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -167,7 +167,7 @@ class LeaveRoomPresenterImplTest {
)
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -191,7 +191,7 @@ class LeaveRoomPresenterImplTest {
)
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts
index 6de297fe77..bef0e77b67 100644
--- a/features/location/api/build.gradle.kts
+++ b/features/location/api/build.gradle.kts
@@ -22,12 +22,12 @@ plugins {
id("kotlin-parcelize")
}
-fun readLocalProperty(name: String) = Properties().apply {
+fun readLocalProperty(name: String): String? = Properties().apply {
try {
load(rootProject.file("local.properties").reader())
} catch (ignored: java.io.IOException) {
}
-}[name]
+}.getProperty(name)
android {
namespace = "io.element.android.features.location.api"
@@ -37,9 +37,23 @@ android {
type = "string",
name = "maptiler_api_key",
value = System.getenv("ELEMENT_ANDROID_MAPTILER_API_KEY")
- ?: readLocalProperty("services.maptiler.apikey") as? String
+ ?: readLocalProperty("services.maptiler.apikey")
?: ""
)
+ resValue(
+ type = "string",
+ name = "maptiler_light_map_id",
+ value = System.getenv("ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID")
+ ?: readLocalProperty("services.maptiler.lightMapId")
+ ?: "basic-v2" // fall back to maptiler's default light map.
+ )
+ resValue(
+ type = "string",
+ name = "maptiler_dark_map_id",
+ value = System.getenv("ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID")
+ ?: readLocalProperty("services.maptiler.darkMapId")
+ ?: "basic-v2-dark" // fall back to maptiler's default dark map.
+ )
}
}
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt
index 14390d0f4f..50d400092a 100644
--- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt
@@ -29,16 +29,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImagePainter
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import io.element.android.features.location.api.internal.StaticMapPlaceholder
-import io.element.android.features.location.api.internal.staticMapUrl
+import io.element.android.features.location.api.internal.StaticMapUrlBuilder
+import io.element.android.features.location.api.internal.centerBottomEdge
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
-import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.theme.ElementTheme
import timber.log.Timber
@@ -65,23 +65,22 @@ fun StaticMapView(
) {
val context = LocalContext.current
var retryHash by remember { mutableStateOf(0) }
+ val builder = remember { StaticMapUrlBuilder(context) }
val painter = rememberAsyncImagePainter(
model = if (constraints.isZero) {
// Avoid building a URL if any of the size constraints is zero (else it will thrown an exception).
null
} else {
- ImageRequest.Builder(LocalContext.current)
+ ImageRequest.Builder(context)
.data(
- staticMapUrl(
- context = context,
+ builder.build(
lat = lat,
lon = lon,
zoom = zoom,
darkMode = darkMode,
- // Size the map based on DP rather than pixels, as otherwise the features and attribution
- // end up being illegibly tiny on high density displays.
- width = constraints.maxWidth.toDp().value.toInt(),
- height = constraints.maxHeight.toDp().value.toInt(),
+ width = constraints.maxWidth,
+ height = constraints.maxHeight,
+ density = LocalDensity.current.density,
)
)
.size(width = constraints.maxWidth, height = constraints.maxHeight)
@@ -106,13 +105,7 @@ fun StaticMapView(
resourceId = DesignSystemR.drawable.pin,
contentDescription = null,
tint = Color.Unspecified,
- modifier = Modifier.align { size, space, _ ->
- // Center bottom edge of pin (i.e. its arrow) to center of screen
- IntOffset(
- x = (space.width - size.width) / 2,
- y = (space.height / 2) - size.height,
- )
- }
+ modifier = Modifier.centerBottomEdge(this),
)
} else {
StaticMapPlaceholder(
@@ -127,7 +120,7 @@ fun StaticMapView(
@DayNightPreviews
@Composable
-fun StaticMapViewPreview() = ElementPreview {
+internal fun StaticMapViewPreview() = ElementPreview {
StaticMapView(
lat = 0.0,
lon = 0.0,
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt
new file mode 100644
index 0000000000..9e20d5179b
--- /dev/null
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import android.content.Context
+import io.element.android.features.location.api.R
+
+internal const val MAPTILER_BASE_URL = "https://api.maptiler.com/maps"
+
+internal fun Context.mapId(darkMode: Boolean) = when (darkMode) {
+ true -> getString(R.string.maptiler_dark_map_id)
+ false -> getString(R.string.maptiler_light_map_id)
+}
+
+internal val Context.apiKey: String
+ get() = getString(R.string.maptiler_api_key)
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt
new file mode 100644
index 0000000000..927e890248
--- /dev/null
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import android.content.Context
+import kotlin.math.roundToInt
+
+/**
+ * Builds an URL for MapTiler's Static Maps API.
+ *
+ * https://docs.maptiler.com/cloud/api/static-maps/
+ */
+internal class MapTilerStaticMapUrlBuilder(
+ private val apiKey: String,
+ private val lightMapId: String,
+ private val darkMapId: String,
+) : StaticMapUrlBuilder {
+
+ constructor(context: Context) : this(
+ apiKey = context.apiKey,
+ lightMapId = context.mapId(darkMode = false),
+ darkMapId = context.mapId(darkMode = true),
+ )
+
+ override fun build(
+ lat: Double,
+ lon: Double,
+ zoom: Double,
+ darkMode: Boolean,
+ width: Int,
+ height: Int,
+ density: Float
+ ): String {
+ val mapId = if (darkMode) darkMapId else lightMapId
+ val finalZoom = zoom.coerceIn(zoomRange)
+
+ // Request @2x density for xhdpi and above (xhdpi == 320dpi == 2x density).
+ val is2x = density >= 2
+
+ // Scale requested width/height according to the reported display density.
+ val (finalWidth, finalHeight) = coerceWidthAndHeight(
+ width = (width / density).roundToInt(),
+ height = (height / density).roundToInt(),
+ is2x = is2x,
+ )
+
+ val scale = if (is2x) "@2x" else ""
+
+ // Since Maptiler doesn't support arbitrary dpi scaling, we stick to 2x sized
+ // images even on displays with density higher than 2x, thereby yielding an
+ // image smaller than the available space in pixels.
+ // The resulting image will have to be scaled to fit the available space in order
+ // to keep the perceived content size constant at the expense of sharpness.
+ return "$MAPTILER_BASE_URL/${mapId}/static/${lon},${lat},${finalZoom}/${finalWidth}x${finalHeight}${scale}.webp?key=${apiKey}&attribution=bottomleft"
+ }
+}
+
+private fun coerceWidthAndHeight(width: Int, height: Int, is2x: Boolean): Pair {
+ if (width <= 0 || height <= 0) {
+ // This effectively yields an URL which asks for a 0x0 image which will result in an HTTP error,
+ // but it's better than e.g. asking for a 1x1 image which would be unreadable and increase usage costs.
+ return 0 to 0
+ }
+ val aspectRatio = width.toDouble() / height.toDouble()
+ val range = if (is2x) widthHeightRange2x else widthHeightRange
+ return if (width >= height) {
+ width.coerceIn(range).let { coercedWidth ->
+ coercedWidth to (coercedWidth / aspectRatio).roundToInt()
+ }
+ } else {
+ height.coerceIn(range).let { coercedHeight ->
+ (coercedHeight * aspectRatio).roundToInt() to coercedHeight
+ }
+ }
+}
+
+private val widthHeightRange = 1..2048 // API will error if outside 1-2048 range @1x.
+private val widthHeightRange2x = 1..1024 // API will error if outside 1-1024 range @2x.
+private val zoomRange = 0.0..22.0 // API will error if outside 0-22 range.
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt
new file mode 100644
index 0000000000..6972e45330
--- /dev/null
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+@file:JvmName("TileServerStyleUriBuilderKt")
+
+package io.element.android.features.location.api.internal
+
+import android.content.Context
+
+internal class MapTilerTileServerStyleUriBuilder(
+ private val apiKey: String,
+ private val lightMapId: String,
+ private val darkMapId: String,
+) : TileServerStyleUriBuilder {
+
+ constructor(context: Context) : this(
+ apiKey = context.apiKey,
+ lightMapId = context.mapId(darkMode = false),
+ darkMapId = context.mapId(darkMode = true),
+ )
+
+ override fun build(darkMode: Boolean): String {
+ val mapId = if (darkMode) darkMapId else lightMapId
+ return "${MAPTILER_BASE_URL}/${mapId}/style.json?key=${apiKey}"
+ }
+}
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt
deleted file mode 100644
index b6f21a4512..0000000000
--- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.features.location.api.internal
-
-import android.content.Context
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
-import io.element.android.features.location.api.R
-import io.element.android.libraries.theme.ElementTheme
-
-/**
- * Provides the URL to an image that contains a statically-generated map of the given location.
- */
-fun staticMapUrl(
- context: Context,
- lat: Double,
- lon: Double,
- zoom: Double,
- width: Int,
- height: Int,
- darkMode: Boolean,
-): String {
- return "${baseUrl(darkMode)}/static/${lon},${lat},${zoom}/${width}x${height}@2x.webp?key=${context.apiKey}&attribution=bottomleft"
-}
-
-/**
- * Utility function to remember the tile server URL based on the current theme.
- */
-@Composable
-fun rememberTileStyleUrl(): String {
- val context = LocalContext.current
- val darkMode = !ElementTheme.isLightTheme
- return remember(darkMode) {
- tileStyleUrl(
- context = context,
- darkMode = darkMode
- )
- }
-}
-
-/**
- * Provides the URL to a MapLibre style document, used for rendering dynamic maps.
- */
-private fun tileStyleUrl(
- context: Context,
- darkMode: Boolean,
-): String {
- return "${baseUrl(darkMode)}/style.json?key=${context.apiKey}"
-}
-
-private fun baseUrl(darkMode: Boolean) =
- "https://api.maptiler.com/maps/" +
- if (darkMode)
- "dea61faf-292b-4774-9660-58fcef89a7f3"
- else
- "9bc819c8-e627-474a-a348-ec144fe3d810"
-
-private val Context.apiKey: String
- get() = getString(R.string.maptiler_api_key)
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt
new file mode 100644
index 0000000000..736f24e609
--- /dev/null
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.IntOffset
+
+/**
+ * Horizontally aligns the content to the center of the space.
+ * Vertically aligns the bottom edge of the content to the center of the space.
+ */
+fun Modifier.centerBottomEdge(scope: BoxScope): Modifier = with(scope) {
+ then(
+ Modifier.align { size, space, _ ->
+ IntOffset(
+ x = (space.width - size.width) / 2,
+ y = space.height / 2 - size.height,
+ )
+ }
+ )
+}
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt
index d36ead5b28..84349d97c9 100644
--- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt
@@ -79,7 +79,7 @@ internal fun StaticMapPlaceholder(
@DayNightPreviews
@Composable
-fun StaticMapPlaceholderPreview(
+internal fun StaticMapPlaceholderPreview(
@PreviewParameter(BooleanParameterProvider::class) values: Boolean
) = ElementPreview {
StaticMapPlaceholder(
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt
new file mode 100644
index 0000000000..8c29b5c13d
--- /dev/null
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import android.content.Context
+
+/**
+ * Builds an URL for a 3rd party service provider static maps API.
+ */
+interface StaticMapUrlBuilder {
+ fun build(
+ lat: Double,
+ lon: Double,
+ zoom: Double,
+ darkMode: Boolean,
+ width: Int,
+ height: Int,
+ density: Float,
+ ): String
+}
+
+fun StaticMapUrlBuilder(context: Context): StaticMapUrlBuilder = MapTilerStaticMapUrlBuilder(context = context)
diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt
new file mode 100644
index 0000000000..ece32cbf5a
--- /dev/null
+++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import io.element.android.libraries.theme.ElementTheme
+
+/**
+ * Builds a style URI for a MapLibre compatible tile server.
+ *
+ * Used for rendering dynamic maps.
+ */
+interface TileServerStyleUriBuilder {
+ fun build(
+ darkMode: Boolean,
+ ): String
+}
+
+fun TileServerStyleUriBuilder(context: Context): TileServerStyleUriBuilder = MapTilerTileServerStyleUriBuilder(context = context)
+
+/**
+ * Provides and remembers a style URI for a MapLibre compatible tile server.
+ *
+ * Used for rendering dynamic maps.
+ */
+@Composable
+fun rememberTileStyleUrl(): String {
+ val context = LocalContext.current
+ val darkMode = !ElementTheme.isLightTheme
+ return remember(darkMode) {
+ TileServerStyleUriBuilder(context).build(darkMode)
+ }
+}
diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt
new file mode 100644
index 0000000000..c359777ee7
--- /dev/null
+++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class MapTilerStaticMapUrlBuilderTest {
+
+ private val builder = MapTilerStaticMapUrlBuilder(
+ apiKey = "anApiKey",
+ lightMapId = "aLightMapId",
+ darkMapId = "aDarkMapId",
+ )
+
+ @Test
+ fun `static map 1x density`() {
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 800,
+ height = 600,
+ density = 1f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600.webp?key=anApiKey&attribution=bottomleft")
+ }
+
+ @Test
+ fun `static map 1,5x density`() {
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 1200,
+ height = 900,
+ density = 1.5f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600.webp?key=anApiKey&attribution=bottomleft")
+ }
+
+ @Test
+ fun `static map 2x density`() {
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 1600,
+ height = 1200,
+ density = 2f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600@2x.webp?key=anApiKey&attribution=bottomleft")
+ }
+
+ @Test
+ fun `static map 3x density`() {
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 2400,
+ height = 1800,
+ density = 3f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600@2x.webp?key=anApiKey&attribution=bottomleft")
+ }
+
+ @Test
+ fun `too big image is coerced keeping aspect ratio`() {
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 4096,
+ height = 2048,
+ density = 1f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/2048x1024.webp?key=anApiKey&attribution=bottomleft")
+
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 2048,
+ height = 4096,
+ density = 1f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x2048.webp?key=anApiKey&attribution=bottomleft")
+
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 4096,
+ height = 2048,
+ density = 2f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x512@2x.webp?key=anApiKey&attribution=bottomleft")
+
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 2048,
+ height = 4096,
+ density = 2f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/512x1024@2x.webp?key=anApiKey&attribution=bottomleft")
+
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = Int.MAX_VALUE,
+ height = Int.MAX_VALUE,
+ density = 2f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x1024@2x.webp?key=anApiKey&attribution=bottomleft")
+ }
+
+ @Test
+ fun `too small image is coerced to 0x0`() {
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 0,
+ height = 0,
+ density = 1f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0.webp?key=anApiKey&attribution=bottomleft")
+
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = 0,
+ height = 0,
+ density = 2f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0@2x.webp?key=anApiKey&attribution=bottomleft")
+
+ assertThat(
+ builder.build(
+ lat = 1.23,
+ lon = -4.56,
+ zoom = 7.8,
+ darkMode = false,
+ width = Int.MIN_VALUE,
+ height = Int.MIN_VALUE,
+ density = 1f,
+ )
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0.webp?key=anApiKey&attribution=bottomleft")
+ }
+}
diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt
new file mode 100644
index 0000000000..abff83c582
--- /dev/null
+++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.location.api.internal
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class MapTilerTileServerStyleUriBuilderTest {
+
+ private val builder = MapTilerTileServerStyleUriBuilder(
+ apiKey = "anApiKey",
+ lightMapId = "aLightMapId",
+ darkMapId = "aDarkMapId",
+ )
+
+ @Test
+ fun `light map uri`() {
+ assertThat(
+ builder.build(darkMode = false)
+ ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/style.json?key=anApiKey")
+ }
+
+ @Test
+ fun `dark map uri`() {
+ assertThat(
+ builder.build(darkMode = true)
+ ).isEqualTo("https://api.maptiler.com/maps/aDarkMapId/style.json?key=anApiKey")
+ }
+}
diff --git a/features/location/impl/build.gradle.kts b/features/location/impl/build.gradle.kts
index 1158b5f152..e808eed11c 100644
--- a/features/location/impl/build.gradle.kts
+++ b/features/location/impl/build.gradle.kts
@@ -45,7 +45,6 @@ dependencies {
implementation(projects.libraries.uiStrings)
implementation(libs.dagger)
implementation(projects.anvilannotations)
- implementation(projects.services.toolbox.api)
anvil(projects.anvilcodegen)
ksp(libs.showkase.processor)
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt
index ac99be1f59..4709c78538 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt
@@ -20,6 +20,7 @@ import android.Manifest
import android.view.Gravity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.ui.graphics.Color
import com.mapbox.mapboxsdk.camera.CameraPosition
import com.mapbox.mapboxsdk.geometry.LatLng
import io.element.android.libraries.maplibre.compose.MapLocationSettings
@@ -53,6 +54,13 @@ object MapDefaults {
val locationSettings: MapLocationSettings
get() = MapLocationSettings(
locationEnabled = false,
+ backgroundTintColor = Color.White,
+ foregroundTintColor = Color.Black,
+ backgroundStaleTintColor = Color.White,
+ foregroundStaleTintColor = Color.Black,
+ accuracyColor = Color.Black,
+ pulseEnabled = true,
+ pulseColor = Color.Black,
)
val centerCameraPosition = CameraPosition.Builder()
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt
index d06124539c..595e26e32e 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt
@@ -36,12 +36,7 @@ import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.services.analytics.api.AnalyticsService
-import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.launch
-import java.time.Instant
-import java.time.ZoneOffset
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
import javax.inject.Inject
class SendLocationPresenter @Inject constructor(
@@ -50,7 +45,6 @@ class SendLocationPresenter @Inject constructor(
private val analyticsService: AnalyticsService,
private val messageComposerContext: MessageComposerContext,
private val locationActions: LocationActions,
- private val systemClock: SystemClock,
private val buildMeta: BuildMeta,
) : Presenter {
@@ -115,7 +109,7 @@ class SendLocationPresenter @Inject constructor(
SendLocationState.Mode.PinLocation -> {
val geoUri = event.cameraPosition.toGeoUri()
room.sendLocation(
- body = generateBody(geoUri, systemClock.epochMillis()),
+ body = generateBody(geoUri),
geoUri = geoUri,
description = null,
zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(),
@@ -134,7 +128,7 @@ class SendLocationPresenter @Inject constructor(
SendLocationState.Mode.SenderLocation -> {
val geoUri = event.toGeoUri()
room.sendLocation(
- body = generateBody(geoUri, systemClock.epochMillis()),
+ body = generateBody(geoUri),
geoUri = geoUri,
description = null,
zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(),
@@ -158,7 +152,4 @@ private fun SendLocationEvents.SendLocation.toGeoUri(): String = location?.toGeo
private fun SendLocationEvents.SendLocation.CameraPosition.toGeoUri(): String = "geo:$lat,$lon"
-private fun generateBody(uri: String, epochMillis: Long): String {
- val timestamp = ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT)
- return "Location was shared at $uri as of $timestamp"
-}
+private fun generateBody(uri: String): String = "Location was shared at $uri"
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt
index 1a30c996a8..cfb30a5523 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt
@@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.LocationSearching
import androidx.compose.material.icons.filled.MyLocation
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -43,12 +42,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.mapbox.mapboxsdk.camera.CameraPosition
import io.element.android.features.location.api.Location
+import io.element.android.features.location.api.internal.centerBottomEdge
import io.element.android.features.location.api.internal.rememberTileStyleUrl
import io.element.android.features.location.impl.MapDefaults
+import io.element.android.features.location.impl.R
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.DayNightPreviews
@@ -156,7 +156,11 @@ fun SendLocationView(
navigateUp()
},
leadingContent = {
- Icon(Icons.Default.LocationOn, null)
+ Icon(
+ resourceId = R.drawable.pin_small,
+ contentDescription = null,
+ tint = Color.Unspecified,
+ )
},
)
Spacer(modifier = Modifier.height(16.dp + navBarPadding))
@@ -201,13 +205,7 @@ fun SendLocationView(
resourceId = DesignSystemR.drawable.pin,
contentDescription = null,
tint = Color.Unspecified,
- modifier = Modifier.align { size, space, _ ->
- // Center bottom edge of pin (i.e. its arrow) to center of screen
- IntOffset(
- x = (space.width - size.width) / 2,
- y = (space.height / 2) - size.height,
- )
- }
+ modifier = Modifier.centerBottomEdge(this),
)
FloatingActionButton(
onClick = { state.eventSink(SendLocationEvents.SwitchToMyLocationMode) },
@@ -226,7 +224,7 @@ fun SendLocationView(
@DayNightPreviews
@Composable
-fun SendLocationViewPreview(
+internal fun SendLocationViewPreview(
@PreviewParameter(SendLocationStateProvider::class) state: SendLocationState
) = ElementPreview {
SendLocationView(
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt
index 1865c6b9a4..73bb2d37a4 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt
@@ -50,6 +50,13 @@ class ShowLocationStateProvider : PreviewParameterProvider {
isTrackMyLocation = false,
eventSink = {},
),
+ ShowLocationState(
+ Location(1.23, 2.34, 4f),
+ description = "For some reason I decided to to write a small essay that wraps at just two lines!",
+ hasLocationPermission = false,
+ isTrackMyLocation = false,
+ eventSink = {},
+ ),
ShowLocationState(
Location(1.23, 2.34, 4f),
description = "For some reason I decided to write a small essay in the location description. " +
diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt
index 7726bbf9cc..0e114a43b9 100644
--- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt
+++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt
@@ -40,7 +40,6 @@ import com.mapbox.mapboxsdk.camera.CameraPosition
import com.mapbox.mapboxsdk.geometry.LatLng
import io.element.android.features.location.api.internal.rememberTileStyleUrl
import io.element.android.features.location.impl.MapDefaults
-import io.element.android.features.location.impl.send.SendLocationState
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
diff --git a/features/location/impl/src/main/res/drawable-night/pin_small.xml b/features/location/impl/src/main/res/drawable-night/pin_small.xml
new file mode 100644
index 0000000000..2e8a54b70e
--- /dev/null
+++ b/features/location/impl/src/main/res/drawable-night/pin_small.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/features/location/impl/src/main/res/drawable/pin_small.xml b/features/location/impl/src/main/res/drawable/pin_small.xml
new file mode 100644
index 0000000000..0e277a1ed2
--- /dev/null
+++ b/features/location/impl/src/main/res/drawable/pin_small.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
index 45c99b556a..0aa89e89ba 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.location.impl.send
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -34,7 +34,6 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.SendLocationInvocation
import io.element.android.libraries.textcomposer.MessageComposerMode
-import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -46,7 +45,6 @@ class SendLocationPresenterTest {
private val fakeAnalyticsService = FakeAnalyticsService()
private val messageComposerContextFake = MessageComposerContextFake()
private val fakeLocationActions = FakeLocationActions()
- private val fakeSystemClock = SystemClock { 0L }
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter(
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
@@ -56,7 +54,6 @@ class SendLocationPresenterTest {
analyticsService = fakeAnalyticsService,
messageComposerContext = messageComposerContextFake,
locationActions = fakeLocationActions,
- systemClock = fakeSystemClock,
buildMeta = fakeBuildMeta,
)
@@ -69,7 +66,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
@@ -96,7 +93,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
@@ -123,7 +120,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
val initialState = awaitItem()
@@ -149,7 +146,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
val initialState = awaitItem()
@@ -175,7 +172,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -206,7 +203,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -234,7 +231,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -265,7 +262,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -292,7 +289,7 @@ class SendLocationPresenterTest {
Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
SendLocationInvocation(
- body = "Location was shared at geo:3.0,4.0;u=5.0 as of 1970-01-01T00:00:00Z",
+ body = "Location was shared at geo:3.0,4.0;u=5.0",
geoUri = "geo:3.0,4.0;u=5.0",
description = null,
zoomLevel = 15,
@@ -322,7 +319,7 @@ class SendLocationPresenterTest {
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -349,7 +346,7 @@ class SendLocationPresenterTest {
Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
SendLocationInvocation(
- body = "Location was shared at geo:0.0,1.0 as of 1970-01-01T00:00:00Z",
+ body = "Location was shared at geo:0.0,1.0",
geoUri = "geo:0.0,1.0",
description = null,
zoomLevel = 15,
@@ -384,7 +381,7 @@ class SendLocationPresenterTest {
)
}
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -431,7 +428,7 @@ class SendLocationPresenterTest {
)
}
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
// Skip initial state
@@ -451,7 +448,7 @@ class SendLocationPresenterTest {
@Test
fun `application name is in state`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
sendLocationPresenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
index 47f0e2ccea..7fff766a9f 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.location.impl.show
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -44,7 +44,7 @@ class ShowLocationPresenterTest {
@Test
fun `emits initial state with no location permission`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -59,7 +59,7 @@ class ShowLocationPresenterTest {
fun `emits initial state with location permission`() = runTest {
permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -74,7 +74,7 @@ class ShowLocationPresenterTest {
fun `emits initial state with partial location permission`() = runTest {
permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.SomeGranted))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -87,7 +87,7 @@ class ShowLocationPresenterTest {
@Test
fun `uses action to share location`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -102,7 +102,7 @@ class ShowLocationPresenterTest {
fun `centers on user location`() = runTest {
permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts
index f9bbb390e6..4a4c6756aa 100644
--- a/features/login/impl/build.gradle.kts
+++ b/features/login/impl/build.gradle.kts
@@ -61,6 +61,4 @@ dependencies {
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.tests.testutils)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt
index 3362beac40..ecc2f996cb 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt
@@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -115,12 +114,12 @@ fun AccountProviderView(
@Preview
@Composable
-fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
+internal fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
ElementPreviewLight { ContentToPreview(item) }
@Preview
@Composable
-fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
+internal fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) =
ElementPreviewDark { ContentToPreview(item) }
@Composable
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt
index f9e9624503..ffa337fba7 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt
@@ -71,12 +71,12 @@ fun ChangeServerView(
@Preview
@Composable
-fun ChangeServerViewLightPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) =
+internal fun ChangeServerViewLightPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun ChangeServerViewDarkPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) =
+internal fun ChangeServerViewDarkPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt
index 5beb14b0b4..6ef5be06ab 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt
@@ -35,7 +35,6 @@ internal fun SlidingSyncNotSupportedDialog(
submitText = stringResource(CommonStrings.action_learn_more),
onSubmitClicked = onLearnMoreClicked,
onCancelClicked = onDismiss,
- emphasizeSubmitButton = true,
title = stringResource(CommonStrings.dialog_title_error),
content = stringResource(R.string.screen_change_server_error_no_sliding_sync_message),
)
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt
index 48c674e0a0..b83c0eb1ac 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt
@@ -41,8 +41,7 @@ class CustomTabHandler @Inject constructor(
if (packageName != null) {
customTabsServiceConnection = object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
- customTabsClient = client
- .also { it.warmup(0L) }
+ customTabsClient = client.apply { warmup(0L) }
prefetchUrl(url)
}
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt
index c1235b76c5..1b7486e814 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt
@@ -100,12 +100,12 @@ fun OidcView(
@Preview
@Composable
-fun OidcViewLightPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) =
+internal fun OidcViewLightPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun OidcViewDarkPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) =
+internal fun OidcViewDarkPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt
index 0f444350c9..0e35f9eda5 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt
@@ -127,12 +127,12 @@ fun ChangeAccountProviderView(
@Preview
@Composable
-fun ChangeAccountProviderViewLightPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) =
+internal fun ChangeAccountProviderViewLightPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun ChangeAccountProviderViewDarkPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) =
+internal fun ChangeAccountProviderViewDarkPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
index 123d3013c7..1a021ad605 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt
@@ -92,7 +92,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor(
} else if (matrixHomeServerDetails.supportsPasswordLogin) {
LoginFlow.PasswordLogin
} else {
- throw IllegalStateException("Unsupported login flow")
+ error("Unsupported login flow")
}
}.getOrThrow()
}.runCatchingUpdatingState(loginFlowAction, errorTransform = ChangeServerError::from)
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt
index 2bc002b3fc..239ee3c6ac 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt
@@ -36,11 +36,10 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
-import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.testtags.TestTags
@@ -87,7 +86,7 @@ fun ConfirmAccountProviderView(
},
footer = {
ButtonColumnMolecule {
- ButtonWithProgress(
+ Button(
text = stringResource(id = R.string.screen_account_provider_continue),
showProgress = isLoading,
onClick = { eventSink.invoke(ConfirmAccountProviderEvents.Continue) },
@@ -97,14 +96,13 @@ fun ConfirmAccountProviderView(
.testTag(TestTags.loginContinue)
)
TextButton(
+ text = stringResource(id = R.string.screen_account_provider_change),
onClick = onChange,
enabled = true,
modifier = Modifier
.fillMaxWidth()
.testTag(TestTags.loginChangeServer)
- ) {
- Text(text = stringResource(id = R.string.screen_account_provider_change))
- }
+ )
}
}
) {
@@ -143,12 +141,12 @@ fun ConfirmAccountProviderView(
@Preview
@Composable
-fun ConfirmAccountProviderViewLightPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) =
+internal fun ConfirmAccountProviderViewLightPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun ConfirmAccountProviderViewDarkPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) =
+internal fun ConfirmAccountProviderViewDarkPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt
index c8fa2f4ad3..cf4e0d8ee5 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt
@@ -30,7 +30,8 @@ data class LoginPasswordState(
) {
val submitEnabled: Boolean
get() = loginAction !is Async.Failure &&
- ((formState.login.isNotEmpty() && formState.password.isNotEmpty()))
+ formState.login.isNotEmpty() &&
+ formState.password.isNotEmpty()
}
@Parcelize
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
index d62506ff75..d06ab86b06 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt
@@ -60,11 +60,11 @@ import io.element.android.features.login.impl.error.loginError
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.components.button.BackButton
-import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
@@ -141,7 +141,7 @@ fun LoginPasswordView(
// Flexible spacing to keep the submit button at the bottom
Spacer(modifier = Modifier.weight(1f))
// Submit
- ButtonWithProgress(
+ Button(
text = stringResource(R.string.screen_login_submit),
showProgress = isLoading,
onClick = ::submit,
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt
index 84c5bf4fac..8cfaae2a9e 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt
@@ -211,12 +211,12 @@ private fun HomeserverData.toAccountProvider(): AccountProvider {
@Preview
@Composable
-fun SearchAccountProviderViewLightPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) =
+internal fun SearchAccountProviderViewLightPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun SearchAccountProviderViewDarkPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) =
+internal fun SearchAccountProviderViewDarkPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListStateProvider.kt
index 5907ff1acf..94a38fa406 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListStateProvider.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListStateProvider.kt
@@ -25,7 +25,7 @@ open class WaitListStateProvider : PreviewParameterProvider {
get() = sequenceOf(
aWaitListState(loginAction = Async.Uninitialized),
aWaitListState(loginAction = Async.Loading()),
- aWaitListState(loginAction = Async.Failure(Throwable())),
+ aWaitListState(loginAction = Async.Failure(Throwable("error"))),
aWaitListState(loginAction = Async.Failure(Throwable(message = "IO_ELEMENT_X_WAIT_LIST"))),
aWaitListState(loginAction = Async.Success(SessionId("@alice:element.io"))),
// Add other state here
diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
index 73fbaf30f9..025a67516d 100644
--- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
+++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt
@@ -29,7 +29,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.widthIn
-import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAbsoluteAlignment
@@ -52,7 +51,6 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text
@@ -141,18 +139,10 @@ private fun WaitListContent(
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
if (state.loginAction !is Async.Success) {
- TextButton(
- onClick = onCancelClicked,
- colors = ButtonDefaults.buttonColors(
- containerColor = Color.White,
- contentColor = Color.Black,
- disabledContainerColor = Color.White,
- disabledContentColor = Color.Black,
- ),
- ) {
- Text(
+ ElementTheme(darkTheme = true) {
+ TextButton(
text = stringResource(CommonStrings.action_cancel),
- style = ElementTheme.typography.aliasButtonText,
+ onClick = onCancelClicked,
)
}
}
@@ -208,22 +198,14 @@ private fun WaitListContent(
}
}
if (state.loginAction is Async.Success) {
- Button(
- onClick = { state.eventSink.invoke(WaitListEvents.Continue) },
- colors = ButtonDefaults.buttonColors(
- containerColor = Color.White,
- contentColor = Color.Black,
- disabledContainerColor = Color.White,
- disabledContentColor = Color.Black,
- ),
- modifier = Modifier
- .fillMaxWidth()
- .align(Alignment.BottomCenter)
- .padding(bottom = 8.dp)
- ) {
- Text(
+ ElementTheme(darkTheme = true) {
+ Button(
text = stringResource(id = CommonStrings.action_continue),
- style = ElementTheme.typography.aliasButtonText,
+ onClick = { state.eventSink.invoke(WaitListEvents.Continue) },
+ modifier = Modifier
+ .fillMaxWidth()
+ .align(Alignment.BottomCenter)
+ .padding(bottom = 8.dp),
)
}
}
diff --git a/features/login/impl/src/main/res/drawable/ic_homeserver.xml b/features/login/impl/src/main/res/drawable/ic_homeserver.xml
deleted file mode 100644
index ee061f7007..0000000000
--- a/features/login/impl/src/main/res/drawable/ic_homeserver.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
diff --git a/features/login/impl/src/main/res/drawable/onboarding_icon_light.png b/features/login/impl/src/main/res/drawable/onboarding_icon_light.png
deleted file mode 100644
index ffd8631c47..0000000000
Binary files a/features/login/impl/src/main/res/drawable/onboarding_icon_light.png and /dev/null differ
diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml
index efc7c0cf3c..965cc30c4e 100644
--- a/features/login/impl/src/main/res/values-de/translations.xml
+++ b/features/login/impl/src/main/res/values-de/translations.xml
@@ -3,13 +3,13 @@
"Kontoanbieter wechseln"
"Weiter"
"Adresse des Homeservers"
- "Geben Sie einen Suchbegriff oder eine Domainadresse ein."
+ "Gib einen Suchbegriff oder eine Domainadresse ein."
"Suche nach einem Unternehmen, einer Community oder einem privaten Server."
- "Finde einen Accountanbieter"
+ "Finde einen Kontoanbieter"
"Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."
"Du bist dabei dich bei %s anzumelden"
"Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."
- "Du bist dabei einen Account auf %s zu erstellen"
+ "Du bist dabei ein Konto auf %s zu erstellen"
"Matrix.org ist ein offenes Netzwerk für sichere, dezentralisierte Kommunikation."
"Andere"
"Verwende einen anderen Kontoanbieter, z. B. deinen eigenen privaten Server oder ein Arbeitskonto."
@@ -31,7 +31,7 @@
"Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation"
"Hier werden deine Konversationen stattfinden — genau so wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."
"Du bist dabei dich bei %1$s anzumelden"
- "Du bist dabei einen Account auf %1$s zu erstellen"
+ "Du bist dabei ein Konto auf %1$s zu erstellen"
"Im Moment besteht eine hohe Nachfrage nach %1$s auf %2$s. Besuche die App in ein paar Tagen wieder und versuche es erneut.
Vielen Dank für deine Geduld!"
diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..33514e9f09
--- /dev/null
+++ b/features/login/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,47 @@
+
+
+ "Переключить аккаунт"
+ "Продолжить"
+ "Адрес домашнего сервера"
+ "Введите поисковый запрос или адрес домена."
+ "Поиск компании, сообщества или частного сервера."
+ "Поиск сервера учетной записи"
+ "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."
+ "Вы собираетесь войти в %s"
+ "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."
+ "Вы собираетесь создать учетную запись на %s"
+ "Matrix.org — это открытая сеть для безопасной децентрализованной связи."
+ "Другое"
+ "Используйте другого поставщика учетных записей, например, собственный частный сервер или рабочую учетную запись."
+ "Сменить поставщика учетной записи"
+ "Нам не удалось связаться с этим домашним сервером. Убедитесь, что вы правильно ввели URL-адрес домашнего сервера. Если URL-адрес указан правильно, обратитесь к администратору домашнего сервера за дополнительной помощью."
+ "В настоящее время этот сервер не поддерживает скользящую синхронизацию."
+ "URL-адрес домашнего сервера"
+ "Вы можете подключиться только к существующему серверу, поддерживающему скользящую синхронизацию. Администратору домашнего сервера потребуется настроить его. %1$s"
+ "Какой адрес у вашего сервера?"
+ "Данная учетная запись была деактивирована."
+ "Неверное имя пользователя и/или пароль"
+ "Это не корректный идентификатор пользователя. Ожидаемый формат: \'@user:homeserver.org\'"
+ "Выбранный домашний сервер не поддерживает пароль или логин OIDC. Пожалуйста, свяжитесь с администратором или выберите другой домашний сервер."
+ "Введите сведения о себе"
+ "Рады видеть вас снова!"
+ "Войти в %1$s"
+ "Сменить учетную запись"
+ "Частный сервер для сотрудников Element."
+ "Matrix — это открытая сеть для безопасной децентрализованной связи."
+ "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."
+ "Вы собираетесь войти в %1$s"
+ "Вы собираетесь создать учетную запись на %1$s"
+ "В настоящее время существует высокий спрос на %1$s на %2$s. Вернитесь в приложение через несколько дней и попробуйте снова.
+
+Спасибо за терпение!"
+ "Добро пожаловать в %1$s!"
+ "Почти готово!"
+ "Вы зарегистрированы!"
+ "Продолжить"
+ "Выберите свой сервер"
+ "Пароль"
+ "Продолжить"
+ "Matrix — это открытая сеть для безопасной децентрализованной связи."
+ "Имя пользователя"
+
diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml
index 2cdaf596e1..2969e4ecb0 100644
--- a/features/login/impl/src/main/res/values-sk/translations.xml
+++ b/features/login/impl/src/main/res/values-sk/translations.xml
@@ -6,9 +6,9 @@
"Zadajte hľadaný výraz alebo adresu domény."
"Vyhľadať spoločnosť, komunitu alebo súkromný server."
"Nájsť poskytovateľa účtu"
- "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."
+ "Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."
"Chystáte sa prihlásiť do %s"
- "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."
+ "Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."
"Chystáte sa vytvoriť účet na %s"
"Matrix.org je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu."
"Iný"
diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..ae2ccae3f5
--- /dev/null
+++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,16 @@
+
+
+ "繼續"
+ "您即將登入%s"
+ "您即將在 %s 建立帳號"
+ "其他"
+ "歡迎回來!"
+ "您即將登入 %1$s"
+ "您即將在 %1$s 建立帳號"
+ "歡迎使用 %1$s!"
+ "繼續"
+ "選擇您的伺服器"
+ "密碼"
+ "繼續"
+ "使用者名稱"
+
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
index 9aefafb382..009ab31dcd 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.login.impl.changeserver
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -36,7 +36,7 @@ class ChangeServerPresenterTest {
FakeAuthenticationService(),
AccountProviderDataSource()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -51,7 +51,7 @@ class ChangeServerPresenterTest {
authenticationService,
AccountProviderDataSource()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -72,7 +72,7 @@ class ChangeServerPresenterTest {
authenticationService,
AccountProviderDataSource()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
index 5756cd13d2..32d1c6918a 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
@@ -18,7 +18,7 @@
package io.element.android.features.login.impl.oidc.webview
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@ class OidcPresenterTest {
A_OIDC_DATA,
FakeAuthenticationService(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -53,7 +53,7 @@ class OidcPresenterTest {
A_OIDC_DATA,
FakeAuthenticationService(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -73,7 +73,7 @@ class OidcPresenterTest {
authenticationService,
)
authenticationService.givenOidcCancelError(A_THROWABLE)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -92,7 +92,7 @@ class OidcPresenterTest {
A_OIDC_DATA,
FakeAuthenticationService(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -110,7 +110,7 @@ class OidcPresenterTest {
A_OIDC_DATA,
FakeAuthenticationService(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -129,7 +129,7 @@ class OidcPresenterTest {
authenticationService,
)
authenticationService.givenLoginError(A_THROWABLE)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
index 086428257a..f807355cb1 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.login.impl.screens.changeaccountprovider
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -37,7 +37,7 @@ class ChangeAccountProviderPresenterTest {
val presenter = ChangeAccountProviderPresenter(
changeServerPresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
index 131d0d9298..76a3ad3d22 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.login.impl.screens.confirmaccountprovider
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@ class ConfirmAccountProviderPresenterTest {
AccountProviderDataSource(),
FakeAuthenticationService(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -58,7 +58,7 @@ class ConfirmAccountProviderPresenterTest {
authServer,
)
authServer.givenHomeserver(A_HOMESERVER)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -82,7 +82,7 @@ class ConfirmAccountProviderPresenterTest {
authServer,
)
authServer.givenHomeserver(A_HOMESERVER_OIDC)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -105,7 +105,7 @@ class ConfirmAccountProviderPresenterTest {
AccountProviderDataSource(),
authServer,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -126,7 +126,7 @@ class ConfirmAccountProviderPresenterTest {
AccountProviderDataSource(),
authenticationService,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
index afd4b542e4..421eb869b0 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.login.impl.screens.loginpassword
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -45,7 +45,7 @@ class LoginPasswordPresenterTest {
accountProviderDataSource,
loginUserStory,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -67,7 +67,7 @@ class LoginPasswordPresenterTest {
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -93,7 +93,7 @@ class LoginPasswordPresenterTest {
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
@@ -122,7 +122,7 @@ class LoginPasswordPresenterTest {
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -150,7 +150,7 @@ class LoginPasswordPresenterTest {
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
index 9163f247f5..ae6ae4d344 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.login.impl.screens.searchaccountprovider
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -46,7 +46,7 @@ class SearchAccountProviderPresenterTest {
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
changeServerPresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -66,7 +66,7 @@ class SearchAccountProviderPresenterTest {
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
changeServerPresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -90,7 +90,7 @@ class SearchAccountProviderPresenterTest {
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
changeServerPresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -125,7 +125,7 @@ class SearchAccountProviderPresenterTest {
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
changeServerPresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -160,7 +160,7 @@ class SearchAccountProviderPresenterTest {
HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest),
changeServerPresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
index 389ac52176..507e8cec8b 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.login.impl.screens.waitlistscreen
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -46,7 +46,7 @@ class WaitListPresenterTest {
authenticationService,
loginUserStory,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -68,7 +68,7 @@ class WaitListPresenterTest {
authenticationService,
loginUserStory,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -97,7 +97,7 @@ class WaitListPresenterTest {
authenticationService,
loginUserStory,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
diff --git a/features/logout/api/src/main/res/values-ru/translations.xml b/features/logout/api/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..3991e27251
--- /dev/null
+++ b/features/logout/api/src/main/res/values-ru/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "Вы уверены, что вы хотите выйти?"
+ "Выйти"
+ "Выполняется выход…"
+ "Выйти"
+ "Выйти"
+
diff --git a/features/logout/api/src/main/res/values-zh-rTW/translations.xml b/features/logout/api/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..df722a9467
--- /dev/null
+++ b/features/logout/api/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "您確定要登出嗎?"
+ "登出"
+ "正在登出…"
+ "登出"
+ "登出"
+
diff --git a/features/logout/impl/build.gradle.kts b/features/logout/impl/build.gradle.kts
index 88d8282875..464695e169 100644
--- a/features/logout/impl/build.gradle.kts
+++ b/features/logout/impl/build.gradle.kts
@@ -48,6 +48,4 @@ dependencies {
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
index bed33006d6..29df521cb1 100644
--- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
+++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.logout.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -34,7 +34,7 @@ class LogoutPreferencePresenterTest {
val presenter = DefaultLogoutPreferencePresenter(
FakeMatrixClient(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -47,7 +47,7 @@ class LogoutPreferencePresenterTest {
val presenter = DefaultLogoutPreferencePresenter(
FakeMatrixClient(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -65,7 +65,7 @@ class LogoutPreferencePresenterTest {
val presenter = DefaultLogoutPreferencePresenter(
matrixClient,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts
index 7488820f7d..4746cff1de 100644
--- a/features/messages/impl/build.gradle.kts
+++ b/features/messages/impl/build.gradle.kts
@@ -34,6 +34,7 @@ dependencies {
anvil(projects.anvilcodegen)
api(projects.features.messages.api)
implementation(projects.features.location.api)
+ implementation(projects.features.poll.api)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
@@ -52,7 +53,6 @@ dependencies {
implementation(libs.coil.compose)
implementation(libs.datetime)
implementation(libs.accompanist.flowlayout)
- implementation(libs.androidx.recyclerview)
implementation(libs.jsoup)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.compose)
@@ -78,6 +78,5 @@ dependencies {
testImplementation(projects.libraries.mediapickers.test)
testImplementation(libs.test.mockk)
- androidTestImplementation(libs.test.junitext)
ksp(libs.showkase.processor)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
index a0a3e3a286..8a374471e3 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
@@ -21,10 +21,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
@@ -41,6 +39,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.TimelinePresenter
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
@@ -48,6 +47,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
@@ -72,10 +72,12 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
+import io.element.android.libraries.matrix.ui.room.canRedactAsState
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
import io.element.android.libraries.textcomposer.MessageComposerMode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import timber.log.Timber
class MessagesPresenter @AssistedInject constructor(
@@ -84,6 +86,7 @@ class MessagesPresenter @AssistedInject constructor(
private val timelinePresenter: TimelinePresenter,
private val actionListPresenter: ActionListPresenter,
private val customReactionPresenter: CustomReactionPresenter,
+ private val reactionSummaryPresenter: ReactionSummaryPresenter,
private val retrySendMenuPresenter: RetrySendMenuPresenter,
private val networkMonitor: NetworkMonitor,
private val snackbarDispatcher: SnackbarDispatcher,
@@ -105,32 +108,31 @@ class MessagesPresenter @AssistedInject constructor(
val timelineState = timelinePresenter.present()
val actionListState = actionListPresenter.present()
val customReactionState = customReactionPresenter.present()
+ val reactionSummaryState = reactionSummaryPresenter.present()
val retryState = retrySendMenuPresenter.present()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
- val roomName by produceState(initialValue = room.displayName, key1 = syncUpdateFlow.value) {
- value = room.displayName
- }
- val roomAvatar by produceState(initialValue = room.avatarData(), key1 = syncUpdateFlow.value) {
- value = room.avatarData()
+ val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value)
+ var roomName: Async by remember { mutableStateOf(Async.Uninitialized) }
+ var roomAvatar: Async by remember { mutableStateOf(Async.Uninitialized) }
+ LaunchedEffect(syncUpdateFlow.value) {
+ withContext(dispatchers.io) {
+ roomName = Async.Success(room.displayName)
+ roomAvatar = Async.Success(room.avatarData())
+ }
}
var hasDismissedInviteDialog by rememberSaveable {
mutableStateOf(false)
}
val inviteProgress = remember { mutableStateOf>(Async.Uninitialized) }
-
- val showReinvitePrompt by remember(
- hasDismissedInviteDialog,
- composerState.hasFocus,
- syncUpdateFlow,
- ) {
- derivedStateOf {
- !hasDismissedInviteDialog && composerState.hasFocus && room.isDirect && room.activeMemberCount == 1L
+ var showReinvitePrompt by remember { mutableStateOf(false) }
+ LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) {
+ withContext(dispatchers.io) {
+ showReinvitePrompt = !hasDismissedInviteDialog && composerState.hasFocus && room.isDirect && room.activeMemberCount == 1L
}
}
-
val networkConnectionStatus by networkMonitor.connectivity.collectAsState()
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
@@ -163,10 +165,12 @@ class MessagesPresenter @AssistedInject constructor(
roomName = roomName,
roomAvatar = roomAvatar,
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
+ userHasPermissionToRedact = userHasPermissionToRedact,
composerState = composerState,
timelineState = timelineState,
actionListState = actionListState,
customReactionState = customReactionState,
+ reactionSummaryState = reactionSummaryState,
retrySendMenuState = retryState,
hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online,
snackbarMessage = snackbarMessage,
@@ -274,6 +278,7 @@ class MessagesPresenter @AssistedInject constructor(
is TimelineItemLocationContent -> AttachmentThumbnailInfo(
type = AttachmentThumbnailType.Location,
)
+ is TimelineItemPollContent, // TODO Polls: handle reply to
is TimelineItemTextBasedContent,
is TimelineItemRedactedContent,
is TimelineItemStateContent,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
index 8a067a3a26..d22d54e7f3 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
@@ -21,6 +21,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListState
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
import io.element.android.features.messages.impl.timeline.TimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.components.avatar.AvatarData
@@ -30,13 +31,15 @@ import io.element.android.libraries.matrix.api.core.RoomId
@Immutable
data class MessagesState(
val roomId: RoomId,
- val roomName: String,
- val roomAvatar: AvatarData,
+ val roomName: Async,
+ val roomAvatar: Async,
val userHasPermissionToSendMessage: Boolean,
+ val userHasPermissionToRedact: Boolean,
val composerState: MessageComposerState,
val timelineState: TimelineState,
val actionListState: ActionListState,
val customReactionState: CustomReactionState,
+ val reactionSummaryState: ReactionSummaryState,
val retrySendMenuState: RetrySendMenuState,
val hasNetworkConnection: Boolean,
val snackbarMessage: SnackbarMessage?,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
index d0ddcf68f4..9b3f5073a1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
@@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.messagecomposer.aMessageCompose
import io.element.android.features.messages.impl.timeline.aTimelineItemList
import io.element.android.features.messages.impl.timeline.aTimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.architecture.Async
@@ -29,6 +30,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.textcomposer.MessageComposerMode
+import kotlinx.collections.immutable.persistentSetOf
open class MessagesStateProvider : PreviewParameterProvider {
override val values: Sequence
@@ -38,14 +40,19 @@ open class MessagesStateProvider : PreviewParameterProvider {
aMessagesState().copy(composerState = aMessageComposerState().copy(showAttachmentSourcePicker = true)),
aMessagesState().copy(userHasPermissionToSendMessage = false),
aMessagesState().copy(showReinvitePrompt = true),
+ aMessagesState().copy(
+ roomName = Async.Uninitialized,
+ roomAvatar = Async.Uninitialized,
+ ),
)
}
fun aMessagesState() = MessagesState(
roomId = RoomId("!id:domain"),
- roomName = "Room name",
- roomAvatar = AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom),
+ roomName = Async.Success("Room name"),
+ roomAvatar = Async.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
userHasPermissionToSendMessage = true,
+ userHasPermissionToRedact = false,
composerState = aMessageComposerState().copy(
text = "Hello",
isFullScreen = false,
@@ -62,6 +69,11 @@ fun aMessagesState() = MessagesState(
customReactionState = CustomReactionState(
selectedEventId = null,
eventSink = {},
+ selectedEmoji = persistentSetOf(),
+ ),
+ reactionSummaryState = ReactionSummaryState(
+ target = null,
+ eventSink = {},
),
hasNetworkConnection = true,
snackbarMessage = null,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
index 6d8f2792e0..b007d59e36 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
@@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.SnackbarHost
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@@ -43,13 +42,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.actionlist.ActionListEvents
import io.element.android.features.messages.impl.actionlist.ActionListView
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
@@ -59,15 +56,19 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
import io.element.android.features.messages.impl.timeline.TimelineView
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
import io.element.android.libraries.androidutils.ui.hideKeyboard
+import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.ProgressDialogType
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@@ -76,6 +77,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.LogCompositions
+import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
@@ -115,7 +117,7 @@ fun MessagesView(
fun onMessageLongClicked(event: TimelineItem.Event) {
Timber.v("OnMessageLongClicked= ${event.id}")
localView.hideKeyboard()
- state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event))
+ state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event, state.userHasPermissionToRedact))
}
fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) {
@@ -127,8 +129,14 @@ fun MessagesView(
state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventId))
}
- fun onMoreReactionsClicked(event: TimelineItem.Event): Unit =
- state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId))
+ fun onEmojiReactionLongClicked(emoji: String, event: TimelineItem.Event) {
+ if (event.eventId == null) return
+ state.reactionSummaryState.eventSink(ReactionSummaryEvents.ShowReactionSummary(event.eventId, event.reactionsState.reactions, emoji))
+ }
+
+ fun onMoreReactionsClicked(event: TimelineItem.Event) {
+ state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event))
+ }
Scaffold(
modifier = modifier,
@@ -137,8 +145,8 @@ fun MessagesView(
Column {
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
MessagesViewTopBar(
- roomTitle = state.roomName,
- roomAvatar = state.roomAvatar,
+ roomName = state.roomName.dataOrNull(),
+ roomAvatar = state.roomAvatar.dataOrNull(),
onBackPressed = onBackPressed,
onRoomDetailsClicked = onRoomDetailsClicked,
)
@@ -159,6 +167,7 @@ fun MessagesView(
}
},
onReactionClicked = ::onEmojiReactionClicked,
+ onReactionLongClicked = ::onEmojiReactionLongClicked,
onMoreReactionsClicked = ::onMoreReactionsClicked,
onSendLocationClicked = onSendLocationClicked,
onSwipeToReply = { targetEvent ->
@@ -178,7 +187,7 @@ fun MessagesView(
state = state.actionListState,
onActionSelected = ::onActionSelected,
onCustomReactionClicked = { event ->
- state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId))
+ state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event))
},
onEmojiReactionClicked = ::onEmojiReactionClicked,
)
@@ -193,6 +202,7 @@ fun MessagesView(
}
)
+ ReactionSummaryView(state = state.reactionSummaryState)
RetrySendMessageMenu(
state = state.retrySendMenuState
)
@@ -203,14 +213,13 @@ fun MessagesView(
}
@Composable
-fun ReinviteDialog(state: MessagesState) {
+private fun ReinviteDialog(state: MessagesState) {
if (state.showReinvitePrompt) {
ConfirmationDialog(
title = stringResource(id = R.string.screen_room_invite_again_alert_title),
content = stringResource(id = R.string.screen_room_invite_again_alert_message),
cancelText = stringResource(id = CommonStrings.action_cancel),
submitText = stringResource(id = CommonStrings.action_invite),
- emphasizeSubmitButton = true,
onSubmitClicked = { state.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) },
onDismiss = { state.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel)) }
)
@@ -240,11 +249,12 @@ private fun AttachmentStateView(
}
@Composable
-fun MessagesViewContent(
+private fun MessagesViewContent(
state: MessagesState,
onMessageClicked: (TimelineItem.Event) -> Unit,
onUserDataClicked: (UserId) -> Unit,
onReactionClicked: (key: String, TimelineItem.Event) -> Unit,
+ onReactionLongClicked: (key: String, TimelineItem.Event) -> Unit,
onMoreReactionsClicked: (TimelineItem.Event) -> Unit,
onMessageLongClicked: (TimelineItem.Event) -> Unit,
onTimestampClicked: (TimelineItem.Event) -> Unit,
@@ -268,6 +278,7 @@ fun MessagesViewContent(
onUserDataClicked = onUserDataClicked,
onTimestampClicked = onTimestampClicked,
onReactionClicked = onReactionClicked,
+ onReactionLongClicked = onReactionLongClicked,
onMoreReactionsClicked = onMoreReactionsClicked,
onSwipeToReply = onSwipeToReply,
)
@@ -288,9 +299,9 @@ fun MessagesViewContent(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun MessagesViewTopBar(
- roomTitle: String,
- roomAvatar: AvatarData,
+private fun MessagesViewTopBar(
+ roomName: String?,
+ roomAvatar: AvatarData?,
modifier: Modifier = Modifier,
onRoomDetailsClicked: () -> Unit = {},
onBackPressed: () -> Unit = {},
@@ -301,17 +312,17 @@ fun MessagesViewTopBar(
BackButton(onClick = onBackPressed)
},
title = {
- Row(
- modifier = Modifier.clickable { onRoomDetailsClicked() },
- verticalAlignment = Alignment.CenterVertically
- ) {
- Avatar(roomAvatar)
- Spacer(modifier = Modifier.width(8.dp))
- Text(
- text = roomTitle,
- style = ElementTheme.typography.fontBodyLgMedium,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis
+ val titleModifier = Modifier.clickable { onRoomDetailsClicked() }
+ if (roomName != null && roomAvatar != null) {
+ RoomAvatarAndNameRow(
+ roomName = roomName,
+ roomAvatar = roomAvatar,
+ modifier = titleModifier
+ )
+ } else {
+ IconTitlePlaceholdersRowMolecule(
+ iconSize = AvatarSize.TimelineRoom.dp,
+ modifier = titleModifier
)
}
},
@@ -320,7 +331,28 @@ fun MessagesViewTopBar(
}
@Composable
-fun CantSendMessageBanner(
+private fun RoomAvatarAndNameRow(
+ roomName: String,
+ roomAvatar: AvatarData,
+ modifier: Modifier = Modifier
+) {
+ Row(
+ modifier = modifier,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Avatar(roomAvatar)
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = roomName,
+ style = ElementTheme.typography.fontBodyLgMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+}
+
+@Composable
+private fun CantSendMessageBanner(
modifier: Modifier = Modifier,
) {
Row(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt
index a6244a72e3..3c796036e7 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt
@@ -20,5 +20,5 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
sealed interface ActionListEvents {
object Clear : ActionListEvents
- data class ComputeForMessage(val event: TimelineItem.Event) : ActionListEvents
+ data class ComputeForMessage(val event: TimelineItem.Event, val canRedact: Boolean) : ActionListEvents
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
index 56e9f48dde..f71c750c22 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
@@ -28,9 +28,9 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.canBeCopied
+import io.element.android.features.messages.impl.timeline.model.event.canReact
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildMeta
-import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -49,13 +49,20 @@ class ActionListPresenter @Inject constructor(
}
val displayEmojiReactions by remember {
- derivedStateOf { (target.value as? ActionListState.Target.Success)?.event?.isRemote == true }
+ derivedStateOf {
+ val event = (target.value as? ActionListState.Target.Success)?.event
+ event?.isRemote == true && event.content.canReact()
+ }
}
fun handleEvents(event: ActionListEvents) {
when (event) {
ActionListEvents.Clear -> target.value = ActionListState.Target.None
- is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(event.event, target)
+ is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(
+ timelineItem = event.event,
+ userCanRedact = event.canRedact,
+ target = target,
+ )
}
}
@@ -66,7 +73,11 @@ class ActionListPresenter @Inject constructor(
)
}
- private fun CoroutineScope.computeForMessage(timelineItem: TimelineItem.Event, target: MutableState) = launch {
+ private fun CoroutineScope.computeForMessage(
+ timelineItem: TimelineItem.Event,
+ userCanRedact: Boolean,
+ target: MutableState
+ ) = launch {
target.value = ActionListState.Target.Loading(timelineItem)
val actions =
when (timelineItem.content) {
@@ -103,7 +114,7 @@ class ActionListPresenter @Inject constructor(
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
- if (timelineItem.isMine) {
+ if (timelineItem.isMine || userCanRedact) {
add(TimelineItemAction.Redact)
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
index fd2ad94345..4838d2fdbf 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem
@@ -61,6 +62,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
@@ -235,6 +237,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
val textContent = remember(event.content) { formatter.format(event) }
when (event.content) {
+ is TimelineItemPollContent, // TODO Polls: handle summary
is TimelineItemTextBasedContent,
is TimelineItemStateContent,
is TimelineItemEncryptedContent,
@@ -342,7 +345,7 @@ internal fun EmojiReactionsRow(
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
- modifier = modifier.padding(horizontal = 28.dp, vertical = 16.dp)
+ modifier = modifier.padding(horizontal = 24.dp, vertical = 16.dp)
) {
// TODO use most recently used emojis here when available from the Rust SDK
val defaultEmojis = sequenceOf(
@@ -352,21 +355,25 @@ internal fun EmojiReactionsRow(
val isHighlighted = highlightedEmojis.contains(emoji)
EmojiButton(emoji, isHighlighted, onEmojiReactionClicked)
}
-
- Icon(
- imageVector = Icons.Outlined.AddReaction,
- contentDescription = "Emojis",
- tint = MaterialTheme.colorScheme.secondary,
+ Box(
modifier = Modifier
- .size(24.dp)
- .align(Alignment.CenterVertically)
- .clickable(
- enabled = true,
- onClick = onCustomReactionClicked,
- indication = rememberRipple(bounded = false, radius = emojiRippleRadius),
- interactionSource = remember { MutableInteractionSource() }
- )
- )
+ .size(48.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.AddReaction,
+ contentDescription = "Emojis",
+ tint = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier
+ .size(24.dp)
+ .clickable(
+ enabled = true,
+ onClick = onCustomReactionClicked,
+ indication = rememberRipple(bounded = false, radius = emojiRippleRadius),
+ interactionSource = remember { MutableInteractionSource() }
+ )
+ )
+ }
}
}
@@ -385,12 +392,13 @@ private fun EmojiButton(
Box(
modifier = modifier
.size(48.dp)
- .background(backgroundColor, RoundedCornerShape(24.dp)),
+ .background(backgroundColor, CircleShape),
+
contentAlignment = Alignment.Center
) {
Text(
emoji,
- fontSize = 28.dp.toSp(),
+ fontSize = 24.dp.toSp(),
color = Color.White,
modifier = Modifier
.clickable(
@@ -405,7 +413,7 @@ private fun EmojiButton(
@DayNightPreviews
@Composable
-fun SheetContentPreview(
+internal fun SheetContentPreview(
@PreviewParameter(ActionListStateProvider::class) state: ActionListState
) = ElementPreview {
SheetContent(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt
index ee41ace4b0..de7f5cd47b 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt
@@ -30,7 +30,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider> by remember { mutableStateOf(SearchBarResultState.NotSearching()) }
val forwardingActionState: MutableState>> = remember { mutableStateOf(Async.Uninitialized) }
- val summaries by client.roomSummaryDataSource.allRooms().collectAsState()
+ val summaries by client.roomListService.allRooms().summaries.collectAsState()
LaunchedEffect(query, summaries) {
val filteredSummaries = summaries.filterIsInstance()
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt
index 7540766097..953a7897f6 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt
@@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.forward
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import kotlinx.collections.immutable.ImmutableList
data class ForwardMessagesState(
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt
index 75aacea616..56d7f63eb1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt
@@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.matrix.api.room.message.RoomMessage
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt
index 230965312e..0f00a6f090 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt
@@ -63,7 +63,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.roomListRoomMessage
import io.element.android.libraries.designsystem.theme.roomListRoomName
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.matrix.ui.components.SelectedRoom
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -82,6 +82,7 @@ fun ForwardMessagesView(
return
}
+ @Suppress("UNUSED_PARAMETER")
fun onRoomRemoved(roomSummaryDetails: RoomSummaryDetails) {
// TODO toggle selection when multi-selection is enabled
state.eventSink(ForwardMessagesEvents.RemoveSelectedRoom)
@@ -122,11 +123,10 @@ fun ForwardMessagesView(
},
actions = {
TextButton(
+ text = stringResource(CommonStrings.action_send),
enabled = state.selectedRooms.isNotEmpty(),
onClick = { state.eventSink(ForwardMessagesEvents.ForwardEvent) }
- ) {
- Text(text = stringResource(CommonStrings.action_send))
- }
+ )
}
)
}
@@ -282,12 +282,12 @@ private fun ForwardingErrorDialog(onDismiss: () -> Unit, modifier: Modifier = Mo
@Preview
@Composable
-fun ForwardMessagesViewLightPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) =
+internal fun ForwardMessagesViewLightPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun ForwardMessagesViewDarkPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) =
+internal fun ForwardMessagesViewDarkPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
index 7cc73ef32e..13a9ff3bee 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
@@ -103,14 +103,17 @@ class MediaViewerPresenter @AssistedInject constructor(
)
.onSuccess {
mediaFile.value = it
- }.mapCatching { mediaFile ->
+ }
+ .mapCatching { mediaFile ->
localMediaFactory.createFromMediaFile(
mediaFile = mediaFile,
mediaInfo = inputs.mediaInfo
)
- }.onSuccess {
+ }
+ .onSuccess {
localMedia.value = Async.Success(it)
- }.onFailure {
+ }
+ .onFailure {
localMedia.value = Async.Failure(it)
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt
index 820a34d8d4..1042261be8 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerStateProvider.kt
@@ -32,7 +32,7 @@ open class MediaViewerStateProvider : PreviewParameterProvider
get() = sequenceOf(
aMediaViewerState(),
aMediaViewerState(Async.Loading()),
- aMediaViewerState(Async.Failure(IllegalStateException())),
+ aMediaViewerState(Async.Failure(IllegalStateException("error"))),
aMediaViewerState(
Async.Success(
LocalMedia(Uri.EMPTY, anImageInfo())
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
index 5964c2ced7..66f15225f7 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt
@@ -34,9 +34,6 @@ import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LinearProgressIndicator
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Snackbar
-import androidx.compose.material3.SnackbarHost
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -57,7 +54,6 @@ import io.element.android.features.messages.impl.media.local.LocalMediaView
import io.element.android.features.messages.impl.media.local.MediaInfo
import io.element.android.features.messages.impl.media.local.rememberLocalMediaViewState
import io.element.android.libraries.architecture.Async
-import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@@ -65,6 +61,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
+import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
@@ -100,15 +97,7 @@ fun MediaViewerView(
eventSink = state.eventSink
)
},
- snackbarHost = {
- SnackbarHost(snackbarHostState) { data ->
- Snackbar(
- snackbarData = data,
- containerColor = MaterialTheme.colorScheme.surfaceVariant,
- contentColor = MaterialTheme.colorScheme.primary
- )
- }
- },
+ snackbarHost = { SnackbarHost(snackbarHostState) },
) {
Column(
modifier = Modifier
@@ -256,7 +245,7 @@ private fun ErrorView(
@Preview
@Composable
-fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) =
+internal fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt
index c8324ec677..b554ef98f4 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt
@@ -83,7 +83,7 @@ internal fun AttachmentsBottomSheet(
onDismissRequest = { isVisible = false }
) {
AttachmentSourcePickerMenu(
- eventSink = state.eventSink,
+ state = state,
onSendLocationClicked = onSendLocationClicked,
)
}
@@ -93,7 +93,7 @@ internal fun AttachmentsBottomSheet(
@OptIn(ExperimentalMaterialApi::class)
@Composable
internal fun AttachmentSourcePickerMenu(
- eventSink: (MessageComposerEvents) -> Unit,
+ state: MessageComposerState,
onSendLocationClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
@@ -102,33 +102,35 @@ internal fun AttachmentSourcePickerMenu(
// .navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044
) {
ListItem(
- modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) },
+ modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) },
icon = { Icon(Icons.Default.Collections, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_gallery)) },
)
ListItem(
- modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) },
+ modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) },
icon = { Icon(Icons.Default.AttachFile, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_files)) },
)
ListItem(
- modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) },
+ modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) },
icon = { Icon(Icons.Default.PhotoCamera, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) },
)
ListItem(
- modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) },
+ modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) },
icon = { Icon(Icons.Default.Videocam, null) },
text = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) },
)
- ListItem(
- modifier = Modifier.clickable {
- eventSink(MessageComposerEvents.PickAttachmentSource.Location)
- onSendLocationClicked()
- },
- icon = { Icon(Icons.Default.LocationOn, null) },
- text = { Text(stringResource(R.string.screen_room_attachment_source_location)) },
- )
+ if (state.canShareLocation) {
+ ListItem(
+ modifier = Modifier.clickable {
+ state.eventSink(MessageComposerEvents.PickAttachmentSource.Location)
+ onSendLocationClicked()
+ },
+ icon = { Icon(Icons.Default.LocationOn, null) },
+ text = { Text(stringResource(R.string.screen_room_attachment_source_location)) },
+ )
+ }
}
}
@@ -136,7 +138,9 @@ internal fun AttachmentSourcePickerMenu(
@Composable
internal fun AttachmentSourcePickerMenuPreview() = ElementPreview {
AttachmentSourcePickerMenu(
- eventSink = {},
+ state = aMessageComposerState(
+ canShareLocation = true,
+ ),
onSendLocationClicked = {},
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
index 020236e890..934a67f2e4 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
@@ -74,6 +74,11 @@ class MessageComposerPresenter @Inject constructor(
mutableStateOf(AttachmentsState.None)
}
+ val canShareLocation = remember { mutableStateOf(false) }
+ LaunchedEffect(Unit) {
+ canShareLocation.value = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)
+ }
+
val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType ->
handlePickedMedia(attachmentsState, uri, mimeType)
}
@@ -140,23 +145,23 @@ class MessageComposerPresenter @Inject constructor(
)
)
}
- MessageComposerEvents.AddAttachment -> localCoroutineScope.launchIfMediaPickerEnabled {
+ MessageComposerEvents.AddAttachment -> localCoroutineScope.launch {
showAttachmentSourcePicker = true
}
MessageComposerEvents.DismissAttachmentMenu -> showAttachmentSourcePicker = false
- MessageComposerEvents.PickAttachmentSource.FromGallery -> localCoroutineScope.launchIfMediaPickerEnabled {
+ MessageComposerEvents.PickAttachmentSource.FromGallery -> localCoroutineScope.launch {
showAttachmentSourcePicker = false
galleryMediaPicker.launch()
}
- MessageComposerEvents.PickAttachmentSource.FromFiles -> localCoroutineScope.launchIfMediaPickerEnabled {
+ MessageComposerEvents.PickAttachmentSource.FromFiles -> localCoroutineScope.launch {
showAttachmentSourcePicker = false
filesPicker.launch()
}
- MessageComposerEvents.PickAttachmentSource.PhotoFromCamera -> localCoroutineScope.launchIfMediaPickerEnabled {
+ MessageComposerEvents.PickAttachmentSource.PhotoFromCamera -> localCoroutineScope.launch {
showAttachmentSourcePicker = false
cameraPhotoPicker.launch()
}
- MessageComposerEvents.PickAttachmentSource.VideoFromCamera -> localCoroutineScope.launchIfMediaPickerEnabled {
+ MessageComposerEvents.PickAttachmentSource.VideoFromCamera -> localCoroutineScope.launch {
showAttachmentSourcePicker = false
cameraVideoPicker.launch()
}
@@ -173,17 +178,12 @@ class MessageComposerPresenter @Inject constructor(
hasFocus = hasFocus.value,
mode = messageComposerContext.composerMode,
showAttachmentSourcePicker = showAttachmentSourcePicker,
+ canShareLocation = canShareLocation.value,
attachmentsState = attachmentsState.value,
eventSink = ::handleEvents
)
}
- private fun CoroutineScope.launchIfMediaPickerEnabled(action: suspend () -> Unit) = launch {
- if (featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)) {
- action()
- }
- }
-
private fun CoroutineScope.sendMessage(
text: String,
updateComposerMode: (newComposerMode: MessageComposerMode) -> Unit,
@@ -268,7 +268,8 @@ class MessageComposerPresenter @Inject constructor(
mediaSender.sendMedia(uri, mimeType, compressIfPossible = false, progressCallback)
.onSuccess {
attachmentState.value = AttachmentsState.None
- }.onFailure {
+ }
+ .onFailure {
val snackbarMessage = SnackbarMessage(sendAttachmentError(it))
snackbarDispatcher.post(snackbarMessage)
attachmentState.value = AttachmentsState.None
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt
index 28ec14ffeb..32faaf9d81 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt
@@ -28,6 +28,7 @@ data class MessageComposerState(
val hasFocus: Boolean,
val mode: MessageComposerMode,
val showAttachmentSourcePicker: Boolean,
+ val canShareLocation: Boolean,
val attachmentsState: AttachmentsState,
val eventSink: (MessageComposerEvents) -> Unit
) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt
index 1934154824..a1fbb7ffa0 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt
@@ -26,12 +26,21 @@ open class MessageComposerStateProvider : PreviewParameterProvider
val key = emojis[index % emojis.size]
- add(AggregatedReaction(key = key, count = 1 + index, isHighlighted = isHighlighted))
+ add(anAggregatedReaction(
+ key = key,
+ count = index + 1,
+ isHighlighted = isHighlighted
+ ))
}
}.toPersistentList()
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
index d7820e707b..6e16a3b92d 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
@@ -81,6 +81,7 @@ fun TimelineView(
onTimestampClicked: (TimelineItem.Event) -> Unit,
onSwipeToReply: (TimelineItem.Event) -> Unit,
onReactionClicked: (emoji: String, TimelineItem.Event) -> Unit,
+ onReactionLongClicked: (emoji: String, TimelineItem.Event) -> Unit,
onMoreReactionsClicked: (TimelineItem.Event) -> Unit,
modifier: Modifier = Modifier,
) {
@@ -94,6 +95,7 @@ fun TimelineView(
val lazyListState = rememberLazyListState()
+ @Suppress("UNUSED_PARAMETER")
fun inReplyToClicked(eventId: EventId) {
// TODO implement this logic once we have support to 'jump to event X' in sliding sync
}
@@ -120,6 +122,7 @@ fun TimelineView(
onUserDataClick = onUserDataClicked,
inReplyToClick = ::inReplyToClicked,
onReactionClick = onReactionClicked,
+ onReactionLongClick = onReactionLongClicked,
onMoreReactionsClick = onMoreReactionsClicked,
onTimestampClicked = onTimestampClicked,
onSwipeToReply = onSwipeToReply,
@@ -137,6 +140,7 @@ fun TimelineView(
}
TimelineScrollHelper(
+ isTimelineEmpty = state.timelineItems.isEmpty(),
lazyListState = lazyListState,
hasNewItems = state.hasNewItems,
onScrollFinishedAt = ::onScrollFinishedAt
@@ -154,6 +158,7 @@ fun TimelineItemRow(
onLongClick: (TimelineItem.Event) -> Unit,
inReplyToClick: (EventId) -> Unit,
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
+ onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
onTimestampClicked: (TimelineItem.Event) -> Unit,
onSwipeToReply: (TimelineItem.Event) -> Unit,
@@ -185,6 +190,7 @@ fun TimelineItemRow(
onUserDataClick = onUserDataClick,
inReplyToClick = inReplyToClick,
onReactionClick = onReactionClick,
+ onReactionLongClick = onReactionLongClick,
onMoreReactionsClick = onMoreReactionsClick,
onTimestampClicked = onTimestampClicked,
onSwipeToReply = { onSwipeToReply(timelineItem) },
@@ -223,6 +229,7 @@ fun TimelineItemRow(
onUserDataClick = onUserDataClick,
onTimestampClicked = onTimestampClicked,
onReactionClick = onReactionClick,
+ onReactionLongClick = onReactionLongClick,
onMoreReactionsClick = onMoreReactionsClick,
onSwipeToReply = {},
)
@@ -236,6 +243,7 @@ fun TimelineItemRow(
@Composable
private fun BoxScope.TimelineScrollHelper(
+ isTimelineEmpty: Boolean,
lazyListState: LazyListState,
hasNewItems: Boolean,
onScrollFinishedAt: (Int) -> Unit,
@@ -253,8 +261,8 @@ private fun BoxScope.TimelineScrollHelper(
}
}
- LaunchedEffect(isScrollFinished) {
- if (isScrollFinished) {
+ LaunchedEffect(isScrollFinished, isTimelineEmpty) {
+ if (isScrollFinished && !isTimelineEmpty) {
// Notify the parent composable about the first visible item index when scrolling finishes
onScrollFinishedAt(lazyListState.firstVisibleItemIndex)
}
@@ -309,7 +317,7 @@ private fun JumpToBottomButton(
@DayNightPreviews
@Composable
-fun TimelineViewPreview(
+internal fun TimelineViewPreview(
@PreviewParameter(TimelineItemEventContentProvider::class) content: TimelineItemEventContent
) = ElementPreview {
val timelineItems = aTimelineItemList(content)
@@ -320,6 +328,7 @@ fun TimelineViewPreview(
onUserDataClicked = {},
onMessageLongClicked = {},
onReactionClicked = { _, _ -> },
+ onReactionLongClicked = { _, _ -> },
onMoreReactionsClicked = {},
onSwipeToReply = {},
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt
index 182965a4e5..effd7f23f0 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt
@@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
@@ -31,6 +32,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
@@ -39,9 +41,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import com.vanniktech.emoji.Emoji
import com.vanniktech.emoji.google.GoogleEmojiProvider
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@@ -49,12 +51,15 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
+import kotlinx.collections.immutable.ImmutableSet
+import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun EmojiPicker(
onEmojiSelected: (Emoji) -> Unit,
+ selectedEmojis: ImmutableSet,
modifier: Modifier = Modifier,
) {
val coroutineScope = rememberCoroutineScope()
@@ -62,7 +67,7 @@ fun EmojiPicker(
val emojiProvider = remember { GoogleEmojiProvider() }
val categories = remember { emojiProvider.categories }
val pagerState = rememberPagerState()
- Column (modifier) {
+ Column(modifier) {
TabRow(
selectedTabIndex = pagerState.currentPage,
) {
@@ -92,12 +97,19 @@ fun EmojiPicker(
modifier = Modifier.fillMaxSize(),
columns = GridCells.Adaptive(minSize = 40.dp),
contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp),
- horizontalArrangement = Arrangement.SpaceEvenly,
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
items(category.emojis, key = { it.unicode }) { item ->
+ val backgroundColor = if (selectedEmojis.contains(item.unicode)) {
+ ElementTheme.colors.bgActionPrimaryRest
+ } else {
+ Color.Transparent
+ }
+
Box(
modifier = Modifier
.size(40.dp)
+ .background(backgroundColor, CircleShape)
.clickable(
enabled = true,
onClick = { onEmojiSelected(item) },
@@ -109,7 +121,8 @@ fun EmojiPicker(
Text(
text = item.unicode,
style = ElementTheme.typography.fontHeadingSmRegular,
- ) }
+ )
+ }
}
}
}
@@ -132,6 +145,7 @@ internal fun EmojiPickerDarkPreview() {
private fun ContentToPreview() {
EmojiPicker(
onEmojiSelected = {},
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier.fillMaxWidth(),
+ selectedEmojis = persistentSetOf("😀", "😄", "😃")
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
index 932dce913c..446846db83 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
@@ -35,7 +35,6 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
index 4bb66f0eac..69c73a68e1 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
@@ -33,7 +33,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.designsystem.theme.components.Surface
private val CORNER_RADIUS = 8.dp
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt
index 8d2eb06f99..930adf36cd 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt
@@ -17,9 +17,10 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
@@ -54,8 +55,10 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@Composable
+@OptIn(ExperimentalFoundationApi::class)
fun MessagesReactionButton(
onClick: () -> Unit,
+ onLongClick: () -> Unit,
content: MessagesReactionsButtonContent,
modifier: Modifier = Modifier,
) {
@@ -82,7 +85,10 @@ fun MessagesReactionButton(
.padding(vertical = 2.dp, horizontal = 2.dp)
// Clip click indicator inside the outer border
.clip(RoundedCornerShape(corner = CornerSize(12.dp)))
- .clickable(onClick = onClick)
+ .combinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick
+ )
// Inner border, to highlight when selected
.border(BorderStroke(1.dp, borderColor), RoundedCornerShape(corner = CornerSize(12.dp)))
.background(buttonColor, RoundedCornerShape(corner = CornerSize(12.dp)))
@@ -107,6 +113,7 @@ sealed class MessagesReactionsButtonContent {
}
private val reactionEmojiLineHeight = 20.sp
+private val addEmojiSize = 16.dp
@Composable
private fun TextContent(
@@ -117,6 +124,7 @@ private fun TextContent(
.height(reactionEmojiLineHeight.toDp()),
text = text,
style = ElementTheme.typography.fontBodyMdRegular,
+ color = ElementTheme.materialColors.primary
)
@Composable
@@ -126,9 +134,10 @@ private fun IconContent(
) = Icon(
imageVector = imageVector,
contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction),
- tint = MaterialTheme.colorScheme.secondary,
+ tint = ElementTheme.materialColors.secondary,
modifier = modifier
- .size(reactionEmojiLineHeight.toDp())
+ .size(addEmojiSize)
+
)
@Composable
@@ -161,7 +170,18 @@ private fun ReactionContent(
internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) = ElementPreview {
MessagesReactionButton(
content = MessagesReactionsButtonContent.Reaction(reaction),
- onClick = {}
+ onClick = {},
+ onLongClick = {}
+ )
+}
+
+@DayNightPreviews
+@Composable
+internal fun MessagesAddReactionButtonPreview() = ElementPreview {
+ MessagesReactionButton(
+ content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
+ onClick = {},
+ onLongClick = {}
)
}
@@ -169,13 +189,10 @@ internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionP
@Composable
internal fun MessagesReactionExtraButtonsPreview() = ElementPreview {
Row {
- MessagesReactionButton(
- content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
- onClick = {}
- )
MessagesReactionButton(
content = MessagesReactionsButtonContent.Text("12 more"),
- onClick = {}
+ onClick = {},
+ onLongClick = {}
)
MessagesReactionButton(
content = MessagesReactionsButtonContent.Reaction(
@@ -183,7 +200,8 @@ internal fun MessagesReactionExtraButtonsPreview() = ElementPreview {
key = "A very long reaction with many characters that should be truncated"
)
),
- onClick = {}
+ onClick = {},
+ onLongClick = {}
)
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
index fbb745c34e..90d3e6cd8c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
@@ -59,7 +59,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.constraintlayout.compose.ConstrainScope
import androidx.constraintlayout.compose.ConstraintLayout
-import com.google.accompanist.flowlayout.FlowMainAxisAlignment
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
@@ -113,6 +112,7 @@ fun TimelineItemEventRow(
inReplyToClick: (EventId) -> Unit,
onTimestampClicked: (TimelineItem.Event) -> Unit,
onReactionClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
+ onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit,
onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit,
onSwipeToReply: () -> Unit,
modifier: Modifier = Modifier
@@ -170,6 +170,7 @@ fun TimelineItemEventRow(
inReplyToClicked = ::inReplyToClicked,
onUserDataClicked = ::onUserDataClicked,
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
+ onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
onMoreReactionsClicked = { onMoreReactionsClick(event) },
)
}
@@ -185,6 +186,7 @@ fun TimelineItemEventRow(
inReplyToClicked = ::inReplyToClicked,
onUserDataClicked = ::onUserDataClicked,
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
+ onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
onMoreReactionsClicked = { onMoreReactionsClick(event) },
)
}
@@ -225,6 +227,7 @@ private fun TimelineItemEventRowContent(
inReplyToClicked: () -> Unit,
onUserDataClicked: () -> Unit,
onReactionClicked: (emoji: String) -> Unit,
+ onReactionLongClicked: (emoji: String) -> Unit,
onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit,
modifier: Modifier = Modifier,
) {
@@ -291,8 +294,9 @@ private fun TimelineItemEventRowContent(
if (event.reactionsState.reactions.isNotEmpty()) {
TimelineItemReactions(
reactionsState = event.reactionsState,
- mainAxisAlignment = if (event.isMine) FlowMainAxisAlignment.End else FlowMainAxisAlignment.Start,
+ isOutgoing = event.isMine,
onReactionClicked = onReactionClicked,
+ onReactionLongClicked = onReactionLongClicked,
onMoreReactionsClicked = { onMoreReactionsClicked(event) },
modifier = Modifier
.constrainAs(reactions) {
@@ -480,7 +484,7 @@ private fun ReplyToContent(
val paddings = if (attachmentThumbnailInfo != null) {
PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp)
} else {
- PaddingValues(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 4.dp)
+ PaddingValues(horizontal = 12.dp, vertical = 4.dp)
}
Row(
modifier
@@ -518,42 +522,46 @@ private fun ReplyToContent(
}
}
-private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready) =
- when (val type = inReplyTo.content.type) {
+private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready): AttachmentThumbnailInfo? {
+ val messageContent = inReplyTo.content as? MessageContent ?: return null
+ return when (val type = messageContent.type) {
is ImageMessageType -> AttachmentThumbnailInfo(
thumbnailSource = type.info?.thumbnailSource,
- textContent = inReplyTo.content.body,
+ textContent = messageContent.body,
type = AttachmentThumbnailType.Image,
blurHash = type.info?.blurhash,
)
is VideoMessageType -> AttachmentThumbnailInfo(
thumbnailSource = type.info?.thumbnailSource,
- textContent = inReplyTo.content.body,
+ textContent = messageContent.body,
type = AttachmentThumbnailType.Video,
blurHash = type.info?.blurhash,
)
is FileMessageType -> AttachmentThumbnailInfo(
thumbnailSource = type.info?.thumbnailSource,
- textContent = inReplyTo.content.body,
+ textContent = messageContent.body,
type = AttachmentThumbnailType.File,
)
is LocationMessageType -> AttachmentThumbnailInfo(
- textContent = inReplyTo.content.body,
+ textContent = messageContent.body,
type = AttachmentThumbnailType.Location,
)
is AudioMessageType -> AttachmentThumbnailInfo(
- textContent = inReplyTo.content.body,
+ textContent = messageContent.body,
type = AttachmentThumbnailType.Audio,
)
else -> null
}
+}
@Composable
-private fun textForInReplyTo(inReplyTo: InReplyTo.Ready) =
- when (inReplyTo.content.type) {
+private fun textForInReplyTo(inReplyTo: InReplyTo.Ready): String {
+ val messageContent = inReplyTo.content as? MessageContent ?: return ""
+ return when (messageContent.type) {
is LocationMessageType -> stringResource(CommonStrings.common_shared_location)
- else -> inReplyTo.content.body
+ else -> messageContent.body
}
+}
@Preview
@Composable
@@ -585,6 +593,7 @@ private fun ContentToPreview() {
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
+ onReactionLongClick = { _, _ -> },
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
@@ -604,6 +613,7 @@ private fun ContentToPreview() {
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
+ onReactionLongClick = { _, _ -> },
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
@@ -650,6 +660,7 @@ private fun ContentToPreviewWithReply() {
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
+ onReactionLongClick = { _, _ -> },
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
@@ -670,6 +681,7 @@ private fun ContentToPreviewWithReply() {
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
+ onReactionLongClick = { _, _ -> },
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
@@ -726,6 +738,7 @@ private fun ContentTimestampToPreview(event: TimelineItem.Event) {
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
+ onReactionLongClick = { _, _ -> },
onMoreReactionsClick = {},
onTimestampClicked = {},
onSwipeToReply = {},
@@ -765,6 +778,7 @@ private fun ContentWithManyReactionsToPreview() {
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
+ onReactionLongClick = { _, _ -> },
onMoreReactionsClick = {},
onSwipeToReply = {},
onTimestampClicked = {},
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt
new file mode 100644
index 0000000000..01800f6348
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AddReaction
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import io.element.android.features.messages.impl.R
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+
+/**
+ * A flow layout for reactions that will show a collapse/expand button when the layout wraps over a defined number of rows.
+ * It displays an add more button when there are greater than 0 reactions and always displays the reaction and add more button
+ * on the same row (moving them both to a new row if necessary).
+ * @param expandButton The expand button
+ * @param addMoreButton The add more button
+ * @param modifier The modifier to apply to this layout
+ * @param itemSpacing The horizontal spacing between items
+ * @param rowSpacing The vertical spacing between rows
+ * @param expanded Whether the layout should display in expanded or collapsed state
+ * @param rowsBeforeCollapsible The number of rows before the collapse/expand button is shown
+ * @param reactions The reaction buttons
+ */
+@Composable
+fun TimelineItemReactionsLayout(
+ expandButton: @Composable () -> Unit,
+ addMoreButton: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ itemSpacing: Dp = 0.dp,
+ rowSpacing: Dp = 0.dp,
+ expanded: Boolean = false,
+ rowsBeforeCollapsible: Int? = 2,
+ reactions: @Composable () -> Unit,
+) {
+ SubcomposeLayout(modifier) { constraints ->
+ // Given the placeables and returns a structure representing
+ // how they should wrap on to multiple rows given the constraints max width.
+ fun calculateRows(placeables: List): List> {
+ val rows = mutableListOf>()
+ var currentRow = mutableListOf()
+ var rowX = 0
+
+ placeables.forEach { placeable ->
+ val horizontalSpacing = if (currentRow.isEmpty()) 0 else itemSpacing.toPx().toInt()
+ // If the current view does not fit on this row bump to the next
+ if (rowX + placeable.width > constraints.maxWidth) {
+ rows.add(currentRow)
+ currentRow = mutableListOf()
+ rowX = 0
+ }
+ rowX += horizontalSpacing + placeable.width
+ currentRow.add(placeable)
+ }
+ // If there are items in the current row remember to append it to the returned value
+ if (currentRow.size > 0) {
+ rows.add(currentRow)
+ }
+ return rows
+ }
+
+ // Used to render the collapsed state, this takes the rows inputted and adds the extra button to the last row,
+ // removing only as many trailing reactions as needed to make space for it.
+ fun replaceTrailingItemsWithButtons(rowsIn: List>, expandButton: Placeable, addMoreButton: Placeable): List> {
+ val rows = rowsIn.toMutableList()
+ val lastRow = rows.last()
+ val buttonsWidth = expandButton.width + itemSpacing.toPx().toInt() + addMoreButton.width
+ var rowX = 0
+ lastRow.forEachIndexed { i, placeable ->
+ val horizontalSpacing = if (i == 0) 0 else itemSpacing.toPx().toInt()
+ rowX += placeable.width + horizontalSpacing
+ if (rowX > constraints.maxWidth - (buttonsWidth + horizontalSpacing)) {
+ val lastRowWithButton = lastRow.take(i) + listOf(expandButton, addMoreButton)
+ rows[rows.size - 1] = lastRowWithButton
+ return rows
+ }
+ }
+ val lastRowWithButton = lastRow + listOf(expandButton, addMoreButton)
+ rows[rows.size - 1] = lastRowWithButton
+ return rows
+ }
+
+ // To prevent the add more and expand buttons from wrapping on to separate lines.
+ // If there is one item on the last line, it moves the expand button down.
+ fun ensureCollapseAndAddMoreButtonsAreOnTheSameRow(rowsIn: List>): List> {
+ val lastRow = rowsIn.last().toMutableList()
+ if (lastRow.size != 1) {
+ return rowsIn
+ }
+ val rows = rowsIn.toMutableList()
+ val secondLastRow = rows[rows.size - 2].toMutableList()
+ val expandButtonPlaceable = secondLastRow.removeLast()
+ lastRow.add(0, expandButtonPlaceable)
+ rows[rows.size - 2] = secondLastRow
+ rows[rows.size - 1] = lastRow
+ return rows
+ }
+
+ /// Given a list of rows place them in the layout.
+ fun layoutRows(rows: List>): MeasureResult {
+ var width = 0
+ var height = 0
+ val placeables = rows.mapIndexed { i, row ->
+ var rowX = 0
+ var rowHeight = 0
+ val verticalSpacing = if (i == 0) 0 else rowSpacing.toPx().toInt()
+ val rowWithPoints = row.mapIndexed { j, placeable ->
+ val horizontalSpacing = if (j == 0) 0 else itemSpacing.toPx().toInt()
+ val point = IntOffset(rowX + horizontalSpacing, height + verticalSpacing)
+ rowX += placeable.width + horizontalSpacing
+ rowHeight = maxOf(rowHeight, placeable.height)
+ Pair(placeable, point)
+ }
+ height += rowHeight + verticalSpacing
+ width = maxOf(width, rowX)
+ rowWithPoints
+ }.flatten()
+
+ return layout(width = width, height = height) {
+ placeables.forEach {
+ val (placeable, origin) = it
+ placeable.placeRelative(origin.x, origin.y)
+ }
+ }
+ }
+
+ var reactionsPlaceables = subcompose(0, reactions).map { it.measure(constraints) }
+ if (reactionsPlaceables.isEmpty()) {
+ return@SubcomposeLayout layoutRows(listOf())
+ }
+ var expandPlaceable = subcompose(1, expandButton).first().measure(constraints)
+ // Enforce all reaction buttons have the same height
+ val maxHeight = (reactionsPlaceables + listOf(expandPlaceable)).maxOf { it.height }
+ val newConstrains = constraints.copy(minHeight = maxHeight)
+ reactionsPlaceables = subcompose(2, reactions).map { it.measure(newConstrains) }
+ expandPlaceable = subcompose(3, expandButton).first().measure(newConstrains)
+ val addMorePlaceable = subcompose(4, addMoreButton).first().measure(newConstrains)
+
+
+ // Calculate the layout of the rows with the reactions button and add more button
+ val reactionsAndAddMore = calculateRows(reactionsPlaceables + listOf(addMorePlaceable))
+ // If we have extended beyond the defined number of rows we are showing the expand/collapse ui
+ if (rowsBeforeCollapsible?.let { reactionsAndAddMore.size > it } == true) {
+ if (expanded) {
+ // Show all subviews with the add more button at the end
+ var reactionsAndButtons = calculateRows(reactionsPlaceables + listOf(expandPlaceable, addMorePlaceable))
+ reactionsAndButtons = ensureCollapseAndAddMoreButtonsAreOnTheSameRow(reactionsAndButtons)
+ layoutRows(reactionsAndButtons)
+ } else {
+ // Truncate to `rowsBeforeCollapsible` number of rows and replace the reactions at the end of the last row with the buttons
+ val collapsedRows = reactionsAndAddMore.take(rowsBeforeCollapsible)
+ val collapsedRowsWithButtons = replaceTrailingItemsWithButtons(collapsedRows, expandPlaceable, addMorePlaceable)
+ layoutRows(collapsedRowsWithButtons)
+ }
+ } else {
+ // Otherwise we are just showing all items without the expand button
+ layoutRows(reactionsAndAddMore)
+ }
+ }
+}
+
+@DayNightPreviews
+@Composable
+internal fun TimelineItemReactionsLayoutPreview() = ElementPreview {
+ TimelineItemReactionsLayout(
+ expanded = false,
+ expandButton = {
+ MessagesReactionButton(
+ content = MessagesReactionsButtonContent.Text(
+ text = stringResource(id = R.string.screen_room_timeline_less_reactions)
+ ),
+ onClick = {},
+ onLongClick = {}
+ )
+ },
+ addMoreButton = {
+ MessagesReactionButton(
+ content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
+ onClick = {},
+ onLongClick = {}
+ )
+ },
+ reactions = {
+ io.element.android.features.messages.impl.timeline.aTimelineItemReactions(count = 18).reactions.forEach {
+ MessagesReactionButton(
+ content = MessagesReactionsButtonContent.Reaction(
+ it
+ ),
+ onClick = {},
+ onLongClick = {}
+ )
+ }
+ }
+ )
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt
index ca0a58da70..a5cdf4b6cc 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt
@@ -19,18 +19,16 @@ package io.element.android.features.messages.impl.timeline.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.pluralStringResource
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
-import com.google.accompanist.flowlayout.FlowMainAxisAlignment
-import com.google.accompanist.flowlayout.FlowRow
import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
@@ -38,162 +36,132 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemReac
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.toPersistentList
-
-/**
- * The maximum number of items that can be displayed before some items will be hidden
- *
- * TODO The threshold should be based on the number of rows, rather than items.
- * Once items would spill onto a third row, they should be hidden.
- * Note this could be particularly worthwhile to handle reactions that are
- * longer than a single character (as annotation keys are free text).
- */
-private const val COLLAPSE_ITEMS_THRESHOLD = 8
@Composable
fun TimelineItemReactions(
reactionsState: TimelineItemReactions,
- mainAxisAlignment: FlowMainAxisAlignment,
+ isOutgoing: Boolean,
onReactionClicked: (emoji: String) -> Unit,
+ onReactionLongClicked: (emoji: String) -> Unit,
onMoreReactionsClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
var expanded: Boolean by rememberSaveable { mutableStateOf(false) }
-
- val reactions by remember(reactionsState, expanded) {
- derivedStateOf {
- val numToDisplay = if (expanded) {
- reactionsState.reactions.count()
- } else {
- COLLAPSE_ITEMS_THRESHOLD
- }
- reactionsState.reactions.take(numToDisplay).toPersistentList()
- }
- }
-
- val expandableState by remember {
- derivedStateOf {
- if (expanded) {
- ExpandableState.Expanded
- } else {
- val hiddenItems = reactionsState.reactions.count() - reactions.count()
- if (hiddenItems > 0) {
- ExpandableState.Collapsed(hidden = hiddenItems)
- } else {
- ExpandableState.None
- }
- }
- }
- }
-
- TimelineItemReactionsView(
- modifier = modifier,
- reactions = reactions,
- expandableState = expandableState,
- mainAxisAlignment = mainAxisAlignment,
- onReactionClick = onReactionClicked,
- onMoreReactionsClick = onMoreReactionsClicked,
- onExpandClick = { expanded = true },
- onCollapseClick = { expanded = false }
- )
-}
-
-private sealed class ExpandableState {
- object None: ExpandableState()
- data class Collapsed(val hidden: Int): ExpandableState()
- object Expanded : ExpandableState()
+ TimelineItemReactionsView(
+ modifier = modifier,
+ reactions = reactionsState.reactions,
+ expanded = expanded,
+ isOutgoing = isOutgoing,
+ onReactionClick = onReactionClicked,
+ onReactionLongClick = onReactionLongClicked,
+ onMoreReactionsClick = onMoreReactionsClicked,
+ onToggleExpandClick = { expanded = !expanded },
+ )
}
@Composable
private fun TimelineItemReactionsView(
reactions: ImmutableList,
- expandableState: ExpandableState,
- mainAxisAlignment: FlowMainAxisAlignment,
+ isOutgoing: Boolean,
+ expanded: Boolean,
onReactionClick: (emoji: String) -> Unit,
+ onReactionLongClick: (emoji: String) -> Unit,
onMoreReactionsClick: () -> Unit,
- onExpandClick: () -> Unit,
- onCollapseClick: () -> Unit,
+ onToggleExpandClick: () -> Unit,
modifier: Modifier = Modifier
-) =
- FlowRow(
- modifier = modifier,
- mainAxisSpacing = 4.dp,
- crossAxisSpacing = 4.dp,
- mainAxisAlignment = mainAxisAlignment,
- ) {
- reactions.forEach { reaction ->
- MessagesReactionButton(
- content = MessagesReactionsButtonContent.Reaction(reaction = reaction),
- onClick = { onReactionClick(reaction.key) }
- )
- }
- when (expandableState) {
- ExpandableState.Expanded ->
+) {
+ // In LTR languages we want an incoming message's reactions to be LRT and outgoing to be RTL.
+ // For RTL languages it should be the opposite.
+ val currentLayout = LocalLayoutDirection.current
+ val reactionsLayoutDirection = if (!isOutgoing) currentLayout
+ else if (currentLayout == LayoutDirection.Ltr)
+ LayoutDirection.Rtl
+ else
+ LayoutDirection.Ltr
+
+ return CompositionLocalProvider(LocalLayoutDirection provides reactionsLayoutDirection) {
+ TimelineItemReactionsLayout(
+ modifier = modifier,
+ itemSpacing = 4.dp,
+ rowSpacing = 4.dp,
+ expanded = expanded,
+ expandButton = {
MessagesReactionButton(
content = MessagesReactionsButtonContent.Text(
- text = stringResource(id = R.string.screen_room_timeline_less_reactions)
+ text = stringResource(id = if (expanded) R.string.screen_room_reactions_show_less else R.string.screen_room_reactions_show_more)
),
- onClick = onCollapseClick,
+ onClick = onToggleExpandClick,
+ onLongClick = {}
)
- is ExpandableState.Collapsed -> {
- val hidden = expandableState.hidden
+ },
+ addMoreButton = {
MessagesReactionButton(
- content = MessagesReactionsButtonContent.Text(
- text = pluralStringResource(id = R.plurals.screen_room_timeline_more_reactions, hidden, hidden)
- ),
- onClick = onExpandClick,
+ content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
+ onClick = onMoreReactionsClick,
+ onLongClick = {}
)
+ },
+ reactions = {
+ reactions.forEach { reaction ->
+ CompositionLocalProvider(LocalLayoutDirection provides currentLayout) {
+ MessagesReactionButton(
+ content = MessagesReactionsButtonContent.Reaction(reaction = reaction),
+ onClick = { onReactionClick(reaction.key) },
+ onLongClick = { onReactionLongClick(reaction.key) }
+ )
+ }
+ }
}
- ExpandableState.None -> {
- // No expand or collapse action available
- }
- }
- MessagesReactionButton(
- content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
- onClick = onMoreReactionsClick
)
}
+}
@DayNightPreviews
@Composable
-fun TimelineItemReactionsViewPreview() = ElementPreview {
+internal fun TimelineItemReactionsViewPreview() = ElementPreview {
ContentToPreview(
- reactions = aTimelineItemReactions(count = 1).reactions,
- expandableState = ExpandableState.None,
+ reactions = aTimelineItemReactions(count = 1).reactions
)
}
@DayNightPreviews
@Composable
-fun TimelineItemReactionsViewCollapsedPreview() = ElementPreview {
+internal fun TimelineItemReactionsViewFewPreview() = ElementPreview {
ContentToPreview(
- reactions = aTimelineItemReactions(count = 3).reactions,
- expandableState = ExpandableState.Collapsed(hidden = 7),
+ reactions = aTimelineItemReactions(count = 3).reactions
)
}
@DayNightPreviews
@Composable
-fun TimelineItemReactionsViewExpandedPreview() = ElementPreview {
+internal fun TimelineItemReactionsViewIncomingPreview() = ElementPreview {
ContentToPreview(
- reactions = aTimelineItemReactions(count = 10).reactions,
- expandableState = ExpandableState.Expanded,
+ reactions = aTimelineItemReactions(count = 18).reactions
+ )
+}
+
+@DayNightPreviews
+@Composable
+internal fun TimelineItemReactionsViewOutgoingPreview() = ElementPreview {
+ ContentToPreview(
+ reactions = aTimelineItemReactions(count = 18).reactions,
+ isOutgoing = true
)
}
@Composable
private fun ContentToPreview(
reactions: ImmutableList,
- expandableState: ExpandableState
+ isOutgoing: Boolean = false
) {
- TimelineItemReactionsView(
- reactions = reactions,
- expandableState = expandableState,
- mainAxisAlignment = FlowMainAxisAlignment.Center,
- onReactionClick = {},
- onMoreReactionsClick = {},
- onExpandClick = {},
- onCollapseClick = {}
+ TimelineItemReactions(
+ reactionsState = TimelineItemReactions(
+ reactions
+ ),
+ isOutgoing = isOutgoing,
+ onReactionClicked = {},
+ onReactionLongClicked = {},
+ onMoreReactionsClicked = {},
)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt
index 70c2a7dc10..d817ec0cd4 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt
@@ -57,7 +57,8 @@ fun CustomReactionBottomSheet(
) {
EmojiPicker(
onEmojiSelected = ::onEmojiSelectedDismiss,
- modifier = Modifier.fillMaxSize()
+ modifier = Modifier.fillMaxSize(),
+ selectedEmojis = state.selectedEmoji,
)
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt
index b7c210553e..a0d69df372 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt
@@ -16,8 +16,8 @@
package io.element.android.features.messages.impl.timeline.components.customreaction
-import io.element.android.libraries.matrix.api.core.EventId
+import io.element.android.features.messages.impl.timeline.model.TimelineItem
sealed interface CustomReactionEvents {
- data class UpdateSelectedEvent(val eventId: EventId?) : CustomReactionEvents
+ data class UpdateSelectedEvent(val event: TimelineItem.Event?) : CustomReactionEvents
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt
index 0a23d42085..f094f2dbc6 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt
@@ -21,22 +21,24 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.libraries.architecture.Presenter
-import io.element.android.libraries.matrix.api.core.EventId
+import kotlinx.collections.immutable.toImmutableSet
import javax.inject.Inject
class CustomReactionPresenter @Inject constructor() : Presenter {
@Composable
override fun present(): CustomReactionState {
- var selectedEventId by remember { mutableStateOf(null) }
+ var selectedEvent by remember { mutableStateOf(null) }
fun handleEvents(event: CustomReactionEvents) {
when (event) {
- is CustomReactionEvents.UpdateSelectedEvent -> selectedEventId = event.eventId
+ is CustomReactionEvents.UpdateSelectedEvent -> selectedEvent = event.event
}
}
- return CustomReactionState(selectedEventId = selectedEventId, eventSink = ::handleEvents)
+ val selectedEmoji = selectedEvent?.reactionsState?.reactions?.mapNotNull { if(it.isHighlighted) it.key else null }.orEmpty().toImmutableSet()
+ return CustomReactionState(selectedEventId = selectedEvent?.eventId, selectedEmoji = selectedEmoji, eventSink = ::handleEvents)
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt
index 6c0c7f3599..9de1642dff 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt
@@ -17,8 +17,10 @@
package io.element.android.features.messages.impl.timeline.components.customreaction
import io.element.android.libraries.matrix.api.core.EventId
+import kotlinx.collections.immutable.ImmutableSet
data class CustomReactionState(
val selectedEventId: EventId?,
+ val selectedEmoji: ImmutableSet,
val eventSink: (CustomReactionEvents) -> Unit,
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt
index d941b8a814..65101183d9 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt
@@ -65,7 +65,7 @@ fun TimelineItem.Event.toExtraPadding(): ExtraPadding {
fun ExtraPadding.getStr(fontSize: TextUnit): String {
if (nbChars == 0) return ""
val timestampFontSize = ElementTheme.typography.fontBodyXsRegular.fontSize // 11.sp
- val nbOfSpaces = ((timestampFontSize.value / fontSize.value) * nbChars).toInt() + 1
+ val nbOfSpaces = (timestampFontSize.value / fontSize.value * nbChars).toInt() + 1
// A space and some unbreakable spaces
return " " + "\u00A0".repeat(nbOfSpaces)
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
index 3df45eb760..d53e3f1e5b 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
@@ -25,6 +25,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
@@ -90,5 +91,10 @@ fun TimelineItemEventContentView(
content = content,
modifier = modifier
)
+ is TimelineItemPollContent -> TimelineItemPollView(
+ content = content,
+ onAnswerSelected = {},
+ modifier = modifier,
+ )
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt
index 9755377b39..cf481a0aec 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt
@@ -30,7 +30,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemEncryptedView(
- content: TimelineItemEncryptedContent,
+ @Suppress("UNUSED_PARAMETER") content: TimelineItemEncryptedContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt
new file mode 100644
index 0000000000..db3503be37
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components.event
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider
+import io.element.android.features.poll.api.ActivePollContentView
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+import kotlinx.collections.immutable.toImmutableList
+
+@Composable
+fun TimelineItemPollView(
+ content: TimelineItemPollContent,
+ onAnswerSelected: (PollAnswer) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ ActivePollContentView(
+ question = content.question,
+ answerItems = content.answerItems.toImmutableList(),
+ pollKind = content.pollKind,
+ onAnswerSelected = onAnswerSelected,
+ modifier = modifier,
+ )
+}
+
+@DayNightPreviews
+@Composable
+internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) =
+ ElementPreview {
+ TimelineItemPollView(
+ content = content,
+ onAnswerSelected = {},
+ )
+ }
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt
index 44917c7d5b..749151b55f 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt
@@ -29,7 +29,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemRedactedView(
- content: TimelineItemRedactedContent,
+ @Suppress("UNUSED_PARAMETER") content: TimelineItemRedactedContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt
index 852428cb92..f053c5a6a0 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt
@@ -29,7 +29,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemUnknownView(
- content: TimelineItemUnknownContent,
+ @Suppress("UNUSED_PARAMETER") content: TimelineItemUnknownContent,
extraPadding: ExtraPadding,
modifier: Modifier = Modifier
) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
index aeb2e7145e..8f8d767835 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt
@@ -18,7 +18,6 @@ package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.Composable
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt
index b2d5dbc7f6..ce853b84b5 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt
@@ -35,7 +35,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Icon
@@ -92,12 +91,12 @@ fun GroupHeaderView(
@Preview
@Composable
-fun GroupHeaderViewLightPreview() =
+internal fun GroupHeaderViewLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
-fun GroupHeaderViewDarkPreview() =
+internal fun GroupHeaderViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
index cb0264cf5d..ecb81a4367 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
@@ -61,7 +61,7 @@ import org.jsoup.nodes.Element
import org.jsoup.nodes.Node
import org.jsoup.nodes.TextNode
-private const val chipId = "chip"
+private const val CHIP_ID = "chip"
@Composable
fun HtmlDocument(
@@ -351,7 +351,7 @@ private fun HtmlMxReply(
Surface(
modifier = modifier
.padding(bottom = 4.dp)
- .offset(x = -(8.dp)),
+ .offset(x = (-8).dp),
color = MaterialTheme.colorScheme.background,
shape = shape,
) {
@@ -544,17 +544,27 @@ private fun AnnotatedString.Builder.appendLink(link: Element) {
pop()
}
is PermalinkData.RoomEmailInviteLink -> {
- appendInlineContent(chipId, link.ownText())
+ safeAppendInlineContent(CHIP_ID, link.ownText())
}
is PermalinkData.RoomLink -> {
- appendInlineContent(chipId, link.ownText())
+ safeAppendInlineContent(CHIP_ID, link.ownText())
}
is PermalinkData.UserLink -> {
- appendInlineContent(chipId, link.ownText())
+ safeAppendInlineContent(CHIP_ID, link.ownText())
}
}
}
+fun AnnotatedString.Builder.safeAppendInlineContent(chipId: String, ownText: String) {
+ if (ownText.isEmpty()) {
+ // alternateText cannot be empty and default parameter value is private,
+ // so just omit the second param here.
+ appendInlineContent(chipId)
+ } else {
+ appendInlineContent(chipId, ownText)
+ }
+}
+
@Composable
private fun HtmlText(
text: AnnotatedString,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt
new file mode 100644
index 0000000000..fdf94f52ce
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components.reactionsummary
+
+import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
+import io.element.android.libraries.matrix.api.core.EventId
+
+sealed interface ReactionSummaryEvents {
+ object Clear : ReactionSummaryEvents
+ data class ShowReactionSummary(val eventId: EventId, val reactions: List, val selectedKey: String) : ReactionSummaryEvents
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt
new file mode 100644
index 0000000000..456ac5f548
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components.reactionsummary
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import io.element.android.libraries.architecture.Presenter
+import io.element.android.libraries.matrix.api.room.MatrixRoom
+import io.element.android.libraries.matrix.api.room.RoomMember
+import io.element.android.libraries.matrix.api.room.roomMembers
+import io.element.android.libraries.matrix.api.user.MatrixUser
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.toImmutableList
+import javax.inject.Inject
+
+class ReactionSummaryPresenter @Inject constructor(
+ private val room: MatrixRoom,
+) : Presenter {
+ @Composable
+ override fun present(): ReactionSummaryState {
+ LaunchedEffect(Unit) {
+ room.updateMembers()
+ }
+
+ val membersState by room.membersStateFlow.collectAsState()
+
+ val target: MutableState = remember {
+ mutableStateOf(null)
+ }
+ val targetWithAvatars = populateSenderAvatars(members = membersState.roomMembers().orEmpty().toImmutableList(), summary = target.value)
+
+ fun handleEvents(event: ReactionSummaryEvents) {
+ when (event) {
+ is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary(
+ reactions = event.reactions,
+ selectedKey = event.selectedKey,
+ selectedEventId = event.eventId
+ )
+ ReactionSummaryEvents.Clear -> target.value = null
+ }
+ }
+ return ReactionSummaryState(
+ target = targetWithAvatars.value,
+ eventSink = ::handleEvents
+ )
+ }
+
+ @Composable
+ private fun populateSenderAvatars(members: ImmutableList, summary: ReactionSummaryState.Summary?) = remember(summary) {
+ derivedStateOf {
+ summary?.let { summary ->
+ summary.copy(reactions = summary.reactions.map { reaction ->
+ reaction.copy(senders = reaction.senders.map { sender ->
+ val member = members.firstOrNull { it.userId == sender.senderId }
+ val user = MatrixUser(
+ userId = sender.senderId,
+ displayName = member?.displayName,
+ avatarUrl = member?.avatarUrl
+ )
+ sender.copy(user = user)
+ })
+ })
+ }
+ }
+ }
+
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt
new file mode 100644
index 0000000000..37e150320b
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components.reactionsummary
+
+import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
+import io.element.android.libraries.matrix.api.core.EventId
+
+data class ReactionSummaryState(
+ val target: Summary?,
+ val eventSink: (ReactionSummaryEvents) -> Unit
+){
+ data class Summary(
+ val reactions: List,
+ val selectedKey: String,
+ val selectedEventId: EventId
+ )
+}
+
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt
new file mode 100644
index 0000000000..d6642922bb
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components.reactionsummary
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
+import io.element.android.libraries.matrix.api.core.EventId
+
+open class ReactionSummaryStateProvider : PreviewParameterProvider {
+ override val values = sequenceOf(anActionListState())
+}
+
+fun anActionListState(): ReactionSummaryState {
+ val reactions = aTimelineItemReactions(8, true).reactions
+ return ReactionSummaryState(
+ target = ReactionSummaryState.Summary(
+ reactions = reactions,
+ selectedKey = reactions[0].key,
+ selectedEventId = EventId("$1234"),
+ ),
+ eventSink = {}
+ )
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt
new file mode 100644
index 0000000000..1228c41a16
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.components.reactionsummary
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
+import io.element.android.libraries.designsystem.components.avatar.Avatar
+import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.components.avatar.AvatarSize
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
+import io.element.android.libraries.designsystem.theme.components.Surface
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.matrix.api.user.MatrixUser
+import io.element.android.libraries.matrix.ui.model.getAvatarData
+import io.element.android.libraries.theme.ElementTheme
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ReactionSummaryView(
+ state: ReactionSummaryState,
+ modifier: Modifier = Modifier,
+) {
+ val sheetState = rememberModalBottomSheetState()
+
+ fun onDismiss() {
+ state.eventSink(ReactionSummaryEvents.Clear)
+ }
+
+ if (state.target != null) {
+ ModalBottomSheet(
+ onDismissRequest = ::onDismiss,
+ sheetState = sheetState,
+ modifier = modifier
+ ) {
+ SheetContent(summary = state.target)
+ }
+ }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun SheetContent(
+ summary: ReactionSummaryState.Summary,
+ modifier: Modifier = Modifier,
+) {
+ val animationScope = rememberCoroutineScope()
+ var selectedReactionKey: String by rememberSaveable { mutableStateOf(summary.selectedKey) }
+ val selectedReactionIndex: Int by remember {
+ derivedStateOf {
+ summary.reactions.indexOfFirst { it.key == selectedReactionKey }
+ }
+ }
+ val pagerState = rememberPagerState(initialPage = selectedReactionIndex)
+ val reactionListState = rememberLazyListState()
+
+ LaunchedEffect(pagerState.currentPage) {
+ selectedReactionKey = summary.reactions[pagerState.currentPage].key
+ val visibleInfo = reactionListState.layoutInfo.visibleItemsInfo
+ if (selectedReactionIndex <= visibleInfo.first().index || selectedReactionIndex >= visibleInfo.last().index) {
+ reactionListState.animateScrollToItem(selectedReactionIndex)
+ }
+ }
+
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ ) {
+ LazyRow(state = reactionListState,
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ contentPadding = PaddingValues(start = 12.dp, end = 12.dp, bottom = 12.dp)
+ ) {
+ items(summary.reactions) { reaction ->
+ AggregatedReactionButton(
+ reaction = reaction,
+ isHighlighted = selectedReactionKey == reaction.key,
+ onClick = {
+ selectedReactionKey = reaction.key
+ animationScope.launch {
+ pagerState.animateScrollToPage(selectedReactionIndex)
+ }
+ }
+ )
+ }
+ }
+ HorizontalPager(state = pagerState, pageCount = summary.reactions.size) { page ->
+ LazyColumn(modifier = Modifier.fillMaxHeight()) {
+ items(summary.reactions[page].senders) { sender ->
+
+ val user = sender.user ?: MatrixUser(userId = sender.senderId)
+
+ SenderRow(
+ avatarData = user.getAvatarData(AvatarSize.UserListItem),
+ name = user.displayName ?: user.userId.value,
+ userId = user.userId.value,
+ sentTime = sender.sentTime
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun AggregatedReactionButton(
+ reaction: AggregatedReaction,
+ isHighlighted: Boolean,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+
+ val buttonColor = if (isHighlighted) {
+ ElementTheme.colors.bgActionPrimaryRest
+ } else {
+ Color.Transparent
+ }
+
+ val textColor = if (isHighlighted) {
+ MaterialTheme.colorScheme.inversePrimary
+ } else {
+ MaterialTheme.colorScheme.primary
+ }
+
+ val roundedCornerShape = RoundedCornerShape(corner = CornerSize(percent = 50))
+ Surface(
+ modifier = modifier
+ .background(buttonColor, roundedCornerShape)
+ .clip(roundedCornerShape)
+ .clickable(onClick = onClick)
+ .padding(vertical = 8.dp, horizontal = 12.dp),
+ color = buttonColor
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier,
+ ) {
+ Text(
+ text = reaction.displayKey,
+ style = ElementTheme.typography.fontBodyMdRegular.copy(
+ fontSize = 20.sp,
+ lineHeight = 25.sp
+ ),
+ )
+ if (reaction.count > 1) {
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = reaction.count.toString(),
+ color = textColor,
+ style = ElementTheme.typography.fontBodyMdRegular.copy(
+ fontSize = 20.sp,
+ lineHeight = 25.sp
+ )
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun SenderRow(
+ avatarData: AvatarData,
+ name: String,
+ userId: String,
+ sentTime: String,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .heightIn(min = 56.dp)
+ .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Avatar(avatarData)
+ Column(
+ modifier = Modifier.padding(start = 12.dp),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.Bottom
+ ) {
+ Text(
+ modifier = Modifier
+ .padding(end = 4.dp)
+ .weight(1f),
+ text = name,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ color = MaterialTheme.colorScheme.primary,
+ style = ElementTheme.typography.fontBodyMdRegular,
+ )
+ Text(
+ text = sentTime,
+ color = MaterialTheme.colorScheme.secondary,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ style = ElementTheme.typography.fontBodySmRegular,
+ )
+ }
+ Text(
+ text = userId,
+ color = MaterialTheme.colorScheme.secondary,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ style = ElementTheme.typography.fontBodySmRegular,
+ )
+ }
+ }
+}
+
+@DayNightPreviews
+@Composable
+internal fun SheetContentPreview(
+ @PreviewParameter(ReactionSummaryStateProvider::class) state: ReactionSummaryState
+) = ElementPreview {
+ SheetContent(summary = state.target as ReactionSummaryState.Summary)
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt
index d344a5e995..cae55de241 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt
@@ -31,7 +31,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -165,6 +164,7 @@ internal fun RetrySendMessageMenuPreviewDark(@PreviewParameter(RetrySendMenuStat
}
}
+@Suppress("UNUSED_PARAMETER")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview(state: RetrySendMenuState) {
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
index 055e4bb876..65af6bdbf5 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt
@@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.R
import io.element.android.libraries.designsystem.preview.DayNightPreviews
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt
index c76d59806d..23e393ae8f 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt
@@ -24,7 +24,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt
deleted file mode 100644
index 9aa3ab5e02..0000000000
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.features.messages.impl.timeline.diff
-
-import androidx.recyclerview.widget.ListUpdateCallback
-import io.element.android.features.messages.impl.timeline.model.TimelineItem
-import io.element.android.features.messages.impl.timeline.util.invalidateLast
-import timber.log.Timber
-
-internal class CacheInvalidator(private val itemStatesCache: MutableList) :
- ListUpdateCallback {
-
- override fun onChanged(position: Int, count: Int, payload: Any?) {
- Timber.d("onChanged(position= $position, count= $count)")
- (position until position + count).forEach {
- // Invalidate cache
- itemStatesCache[it] = null
- }
- }
-
- override fun onMoved(fromPosition: Int, toPosition: Int) {
- Timber.d("onMoved(fromPosition= $fromPosition, toPosition= $toPosition)")
- val model = itemStatesCache.removeAt(fromPosition)
- itemStatesCache.add(toPosition, model)
- }
-
- override fun onInserted(position: Int, count: Int) {
- Timber.d("onInserted(position= $position, count= $count)")
- itemStatesCache.invalidateLast()
- repeat(count) {
- itemStatesCache.add(position, null)
- }
- }
-
- override fun onRemoved(position: Int, count: Int) {
- Timber.d("onRemoved(position= $position, count= $count)")
- itemStatesCache.invalidateLast()
- repeat(count) {
- itemStatesCache.removeAt(position)
- }
- }
-}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt
new file mode 100644
index 0000000000..a7a3bea00e
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.diff
+
+import io.element.android.features.messages.impl.timeline.model.TimelineItem
+import io.element.android.libraries.androidutils.diff.DefaultDiffCacheInvalidator
+import io.element.android.libraries.androidutils.diff.DiffCacheInvalidator
+import io.element.android.libraries.androidutils.diff.MutableDiffCache
+
+/**
+ * [DiffCacheInvalidator] implementation for [TimelineItem].
+ * It uses [DefaultDiffCacheInvalidator] and invalidate the cache around the updated item so that those items are computed again.
+ * This is needed because a timeline item is computed based on the previous and next items.
+ */
+internal class TimelineItemsCacheInvalidator : DiffCacheInvalidator {
+
+ private val delegate = DefaultDiffCacheInvalidator()
+
+ override fun onChanged(position: Int, count: Int, cache: MutableDiffCache) {
+ delegate.onChanged(position, count, cache)
+ }
+
+ override fun onMoved(fromPosition: Int, toPosition: Int, cache: MutableDiffCache) {
+ delegate.onMoved(fromPosition, toPosition, cache)
+ }
+
+ override fun onInserted(position: Int, count: Int, cache: MutableDiffCache) {
+ cache.invalidateAround(position)
+ delegate.onInserted(position, count, cache)
+ }
+
+ override fun onRemoved(position: Int, count: Int, cache: MutableDiffCache) {
+ cache.invalidateAround(position)
+ delegate.onRemoved(position, count, cache)
+ }
+}
+
+/**
+ * Invalidate the cache around the given position.
+ * It invalidates the previous and next items.
+ */
+private fun MutableDiffCache<*>.invalidateAround(position: Int) {
+ if (position > 0) {
+ set(position - 1, null)
+ }
+ if (position < indices().last) {
+ set(position + 1, null)
+ }
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt
index aa9786c945..d664d3f26c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt
@@ -19,13 +19,13 @@ package io.element.android.features.messages.impl.timeline.factories
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
-import androidx.recyclerview.widget.DiffUtil
-import io.element.android.features.messages.impl.timeline.diff.CacheInvalidator
-import io.element.android.features.messages.impl.timeline.diff.MatrixTimelineItemsDiffCallback
+import io.element.android.features.messages.impl.timeline.diff.TimelineItemsCacheInvalidator
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
import io.element.android.features.messages.impl.timeline.model.TimelineItem
+import io.element.android.libraries.androidutils.diff.DiffCacheUpdater
+import io.element.android.libraries.androidutils.diff.MutableListDiffCache
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import kotlinx.collections.immutable.ImmutableList
@@ -35,9 +35,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
-import timber.log.Timber
import javax.inject.Inject
-import kotlin.system.measureTimeMillis
class TimelineItemsFactory @Inject constructor(
private val dispatchers: CoroutineDispatchers,
@@ -46,13 +44,20 @@ class TimelineItemsFactory @Inject constructor(
private val timelineItemGrouper: TimelineItemGrouper,
) {
private val timelineItems = MutableStateFlow(persistentListOf())
- private val timelineItemsCache = arrayListOf()
-
- // Items from rust sdk, used for diffing
- private var matrixTimelineItems: List = emptyList()
private val lock = Mutex()
- private val cacheInvalidator = CacheInvalidator(timelineItemsCache)
+ private val diffCache = MutableListDiffCache()
+ private val diffCacheUpdater = DiffCacheUpdater(
+ diffCache = diffCache,
+ detectMoves = false,
+ cacheInvalidator = TimelineItemsCacheInvalidator()
+ ) { old, new ->
+ if (old is MatrixTimelineItem.Event && new is MatrixTimelineItem.Event) {
+ old.uniqueId == new.uniqueId
+ } else {
+ false
+ }
+ }
@Composable
fun collectItemsAsState(): State> {
@@ -63,15 +68,15 @@ class TimelineItemsFactory @Inject constructor(
timelineItems: List,
) = withContext(dispatchers.computation) {
lock.withLock {
- calculateAndApplyDiff(timelineItems)
+ diffCacheUpdater.updateWith(timelineItems)
buildAndEmitTimelineItemStates(timelineItems)
}
}
private suspend fun buildAndEmitTimelineItemStates(timelineItems: List) {
val newTimelineItemStates = ArrayList()
- for (index in timelineItemsCache.indices.reversed()) {
- val cacheItem = timelineItemsCache[index]
+ for (index in diffCache.indices().reversed()) {
+ val cacheItem = diffCache.get(index)
if (cacheItem == null) {
buildAndCacheItem(timelineItems, index)?.also { timelineItemState ->
newTimelineItemStates.add(timelineItemState)
@@ -84,20 +89,6 @@ class TimelineItemsFactory @Inject constructor(
this.timelineItems.emit(result)
}
- private fun calculateAndApplyDiff(newTimelineItems: List) {
- val timeToDiff = measureTimeMillis {
- val diffCallback =
- MatrixTimelineItemsDiffCallback(
- oldList = matrixTimelineItems,
- newList = newTimelineItems
- )
- val diffResult = DiffUtil.calculateDiff(diffCallback, false)
- matrixTimelineItems = newTimelineItems
- diffResult.dispatchUpdatesTo(cacheInvalidator)
- }
- Timber.v("Time to apply diff on new list of ${newTimelineItems.size} items: $timeToDiff ms")
- }
-
private fun buildAndCacheItem(
timelineItems: List,
index: Int
@@ -108,7 +99,7 @@ class TimelineItemsFactory @Inject constructor(
is MatrixTimelineItem.Virtual -> virtualItemFactory.create(currentTimelineItem)
MatrixTimelineItem.Other -> null
}
- timelineItemsCache[index] = timelineItemState
+ diffCache[index] = timelineItemState
return timelineItemState
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
index eb6d0e45c0..fad1ccc822 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt
@@ -22,6 +22,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventTimeline
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
@@ -35,6 +37,8 @@ class TimelineItemContentFactory @Inject constructor(
private val messageFactory: TimelineItemContentMessageFactory,
private val redactedMessageFactory: TimelineItemContentRedactedFactory,
private val stickerFactory: TimelineItemContentStickerFactory,
+ private val pollFactory: TimelineItemContentPollFactory,
+ private val pollEndFactory: TimelineItemContentPollEndFactory,
private val utdFactory: TimelineItemContentUTDFactory,
private val roomMembershipFactory: TimelineItemContentRoomMembershipFactory,
private val profileChangeFactory: TimelineItemContentProfileChangeFactory,
@@ -53,6 +57,8 @@ class TimelineItemContentFactory @Inject constructor(
is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem)
is StateContent -> stateFactory.create(eventTimelineItem)
is StickerContent -> stickerFactory.create(itemContent)
+ is PollContent -> pollFactory.create(itemContent)
+ is PollEndContent -> pollEndFactory.create(itemContent)
is UnableToDecryptContent -> utdFactory.create(itemContent)
is UnknownContent -> TimelineItemUnknownContent
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt
index 4dacb76523..e764cfa28f 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt
@@ -23,7 +23,7 @@ import javax.inject.Inject
class TimelineItemContentFailedToParseMessageFactory @Inject constructor() {
- fun create(failedToParseMessageLike: FailedToParseMessageLikeContent): TimelineItemEventContent {
+ fun create(@Suppress("UNUSED_PARAMETER") failedToParseMessageLike: FailedToParseMessageLikeContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt
index 7e2bf90050..f7d3c10483 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt
@@ -23,6 +23,7 @@ import javax.inject.Inject
class TimelineItemContentFailedToParseStateFactory @Inject constructor() {
+ @Suppress("UNUSED_PARAMETER")
fun create(failedToParseState: FailedToParseStateContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt
new file mode 100644
index 0000000000..ff9eb837b6
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.factories.event
+
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent
+import javax.inject.Inject
+
+class TimelineItemContentPollEndFactory @Inject constructor() {
+
+ fun create(@Suppress("UNUSED_PARAMETER") content: PollEndContent): TimelineItemEventContent {
+ return TimelineItemUnknownContent
+ }
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt
new file mode 100644
index 0000000000..2ab69f10e4
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.factories.event
+
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
+import io.element.android.features.poll.api.PollAnswerItem
+import io.element.android.libraries.matrix.api.MatrixClient
+import io.element.android.libraries.matrix.api.poll.PollKind
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
+import javax.inject.Inject
+
+class TimelineItemContentPollFactory @Inject constructor(
+ private val matrixClient: MatrixClient,
+) {
+
+ fun create(content: PollContent): TimelineItemEventContent {
+ // Todo Move this computation to the matrix rust sdk
+ val showResults = content.kind == PollKind.Disclosed && matrixClient.sessionId in content.votes.flatMap { it.value }
+ val pollVotesCount = content.votes.flatMap { it.value }.size
+ val userVotes = content.votes.filter { matrixClient.sessionId in it.value }.keys
+ val answerItems = content.answers.map { answer ->
+ val votesCount = content.votes[answer.id]?.size ?: 0
+ val progress = if (pollVotesCount > 0) votesCount.toFloat() / pollVotesCount.toFloat() else 0f
+ PollAnswerItem(
+ answer = answer,
+ isSelected = answer.id in userVotes,
+ isDisclosed = showResults,
+ votesCount = votesCount,
+ progress = progress,
+ )
+ }
+
+ return TimelineItemPollContent(
+ question = content.question,
+ answerItems = answerItems,
+ votes = content.votes,
+ pollKind = content.kind,
+ isDisclosed = showResults
+ )
+ }
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt
index dfbbd233c7..9419c7c31d 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt
@@ -23,7 +23,7 @@ import javax.inject.Inject
class TimelineItemContentRedactedFactory @Inject constructor() {
- fun create(content: RedactedContent): TimelineItemEventContent {
+ fun create(@Suppress("UNUSED_PARAMETER") content: RedactedContent): TimelineItemEventContent {
return TimelineItemRedactedContent
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt
index b823791912..1607bb77c4 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt
@@ -23,7 +23,7 @@ import javax.inject.Inject
class TimelineItemContentStickerFactory @Inject constructor() {
- fun create(content: StickerContent): TimelineItemEventContent {
+ fun create(@Suppress("UNUSED_PARAMETER") content: StickerContent): TimelineItemEventContent {
return TimelineItemUnknownContent
}
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
index 6bc5df1e79..683b7515b9 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt
@@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.factories.event
import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
+import io.element.android.features.messages.impl.timeline.model.AggregatedReactionSender
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
@@ -90,14 +91,34 @@ class TimelineItemEventFactory @Inject constructor(
}
private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions {
- val aggregatedReactions = event.reactions.map {
+ val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT)
+ var aggregatedReactions = event.reactions.map { reaction ->
+ // Sort reactions within an aggregation by timestamp descending.
+ // This puts the most recent at the top, useful in cases like the
+ // reaction summary view or getting the most recent reaction.
AggregatedReaction(
- key = it.key,
- count = it.count.toInt(),
- isHighlighted = it.senderIds.contains(matrixClient.sessionId),
+ key = reaction.key,
+ currentUserId = matrixClient.sessionId,
+ senders = reaction.senders
+ .sortedByDescending{ it.timestamp }
+ .map {
+ val date = Date(it.timestamp)
+ AggregatedReactionSender(
+ senderId = it.senderId,
+ timestamp = date,
+ sentTime = timeFormatter.format(date),
+ )
+ }
)
}
- aggregatedReactions.sortedByDescending { it.count }
+ // Sort aggregated reactions by count and then timestamp ascending, using
+ // the most recent reaction in the aggregation(hence index 0).
+ // This appends new aggregations on the end of the reaction layout.
+ aggregatedReactions = aggregatedReactions
+ .sortedWith(
+ compareByDescending { it.count }
+ .thenBy { it.senders[0].timestamp }
+ )
return TimelineItemReactions(aggregatedReactions.toImmutableList())
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt
index 0b8baf692a..1d2dec09b7 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt
@@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent
@@ -33,6 +34,8 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
@@ -55,6 +58,7 @@ internal fun TimelineItem.Event.canBeGrouped(): Boolean {
is TimelineItemVideoContent,
is TimelineItemAudioContent,
is TimelineItemLocationContent,
+ is TimelineItemPollContent,
TimelineItemRedactedContent,
TimelineItemUnknownContent -> false
is TimelineItemProfileChangeContent,
@@ -74,6 +78,8 @@ internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean {
is MessageContent,
RedactedContent,
is StickerContent,
+ is PollContent,
+ is PollEndContent,
is UnableToDecryptContent -> true
is FailedToParseStateContent,
is ProfileChangeContent,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt
index ba13896c06..59c52ed8cf 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt
@@ -17,6 +17,7 @@
package io.element.android.features.messages.impl.timeline.model
import io.element.android.libraries.core.extensions.ellipsize
+import io.element.android.libraries.matrix.api.core.UserId
/**
* Length at which we ellipsize a reaction key for display
@@ -27,16 +28,15 @@ import io.element.android.libraries.core.extensions.ellipsize
private const val MAX_DISPLAY_CHARS = 16
/**
+ * @property currentUserId the ID of the currently logged in user
* @property key the full reaction key (e.g. "👍", "YES!")
- * @property count the number of users who reacted with this key
- * @property isHighlighted true if the reaction has (also) been sent by the current user.
+ * @property senders the list of users who sent the reactions
*/
data class AggregatedReaction(
+ val currentUserId: UserId,
val key: String,
- val count: Int,
- val isHighlighted: Boolean = false
+ val senders: List
) {
-
/**
* The key to be displayed on screen.
*
@@ -45,4 +45,18 @@ data class AggregatedReaction(
val displayKey: String by lazy {
key.ellipsize(MAX_DISPLAY_CHARS)
}
+
+ /**
+ * The number of users who reacted with this key.
+ */
+ val count: Int by lazy {
+ senders.count()
+ }
+
+ /**
+ * True if the reaction has (also) been sent by the current user.
+ */
+ val isHighlighted: Boolean by lazy {
+ senders.any { it.senderId.value == currentUserId.value }
+ }
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt
index 148f565911..dcd6bb105c 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt
@@ -17,6 +17,9 @@
package io.element.android.features.messages.impl.timeline.model
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.libraries.matrix.api.core.UserId
+import java.text.DateFormat
+import java.util.Date
open class AggregatedReactionProvider : PreviewParameterProvider {
override val values: Sequence
@@ -29,11 +32,27 @@ open class AggregatedReactionProvider : PreviewParameterProvider
+ val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT)
+ val date = Date(1_689_061_264L)
+ add(
+ AggregatedReactionSender(
+ senderId = if (isHighlighted && index == 0) userId else UserId("@user$index:server.org"),
+ timestamp = date,
+ sentTime = timeFormatter.format(date),
+ )
+ )
+ }
+ }
+ return AggregatedReaction(
+ currentUserId = userId,
+ key = key,
+ senders = senders
+ )
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt
new file mode 100644
index 0000000000..276ee0b266
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.model
+
+import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.libraries.matrix.api.user.MatrixUser
+import java.util.Date
+
+data class AggregatedReactionSender(
+ val senderId: UserId,
+ val timestamp: Date,
+ val sentTime: String,
+ val user: MatrixUser? = null
+)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt
index 0ff67e481f..02837bd6b4 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt
@@ -33,3 +33,21 @@ fun TimelineItemEventContent.canBeCopied(): Boolean =
is TimelineItemRedactedContent -> true
else -> false
}
+
+/**
+ * Return true if user can react (i.e. send a reaction) on the event content.
+ */
+fun TimelineItemEventContent.canReact(): Boolean =
+ when (this) {
+ is TimelineItemTextBasedContent,
+ is TimelineItemAudioContent,
+ is TimelineItemEncryptedContent,
+ is TimelineItemFileContent,
+ is TimelineItemImageContent,
+ is TimelineItemLocationContent,
+ is TimelineItemPollContent,
+ is TimelineItemVideoContent -> true
+ is TimelineItemStateContent,
+ is TimelineItemRedactedContent,
+ TimelineItemUnknownContent -> false
+ }
diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt
similarity index 52%
rename from app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt
rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt
index 5a641d75c6..b8a2fa8bca 100644
--- a/app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt
@@ -14,22 +14,18 @@
* limitations under the License.
*/
-package io.element.android.x.initializer
+package io.element.android.features.messages.impl.timeline.model.event
-import android.content.Context
-import androidx.startup.Initializer
-import io.element.android.features.rageshake.impl.logs.VectorFileLogger
-import io.element.android.x.BuildConfig
-import timber.log.Timber
+import io.element.android.features.poll.api.PollAnswerItem
+import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.libraries.matrix.api.poll.PollKind
-class TimberInitializer : Initializer {
-
- override fun create(context: Context) {
- if (BuildConfig.DEBUG) {
- Timber.plant(Timber.DebugTree())
- }
- Timber.plant(VectorFileLogger(context))
- }
-
- override fun dependencies(): List>> = emptyList()
+data class TimelineItemPollContent(
+ val question: String,
+ val answerItems: List,
+ val votes: Map>,
+ val pollKind: PollKind,
+ val isDisclosed: Boolean,
+) : TimelineItemEventContent {
+ override val type: String = "TimelineItemPollContent"
}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt
new file mode 100644
index 0000000000..665d507ead
--- /dev/null
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.impl.timeline.model.event
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.features.poll.api.aPollAnswerItemList
+import io.element.android.libraries.matrix.api.poll.PollKind
+
+open class TimelineItemPollContentProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ aTimelineItemPollContent(),
+ aTimelineItemPollContent().copy(isDisclosed = true),
+ )
+}
+
+fun aTimelineItemPollContent(): TimelineItemPollContent {
+ return TimelineItemPollContent(
+ pollKind = PollKind.Disclosed,
+ isDisclosed = false,
+ question = "What type of food should we have at the party?",
+ answerItems = aPollAnswerItemList(),
+ votes = emptyMap(),
+ )
+}
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt
index 42c50bbd9d..2b35eeda37 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt
@@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
+import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
@@ -47,6 +48,7 @@ class MessageSummaryFormatterImpl @Inject constructor(
is TimelineItemLocationContent -> context.getString(CommonStrings.common_shared_location)
is TimelineItemEncryptedContent -> context.getString(CommonStrings.common_unable_to_decrypt)
is TimelineItemRedactedContent -> context.getString(CommonStrings.common_message_removed)
+ is TimelineItemPollContent, // Todo Polls: handle summary
is TimelineItemUnknownContent -> context.getString(CommonStrings.common_unsupported_event)
is TimelineItemImageContent -> context.getString(CommonStrings.common_image)
is TimelineItemVideoContent -> context.getString(CommonStrings.common_video)
diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml
index 1486e35726..84c903844e 100644
--- a/features/messages/impl/src/main/res/values-de/translations.xml
+++ b/features/messages/impl/src/main/res/values-de/translations.xml
@@ -20,6 +20,6 @@
"Mehr anzeigen"
"Erneut senden"
"Ihre Nachricht konnte nicht gesendet werden"
- "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuchen Sie es erneut."
+ "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."
"Entfernen"
diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml
index 03dcaffac7..5f9f0223aa 100644
--- a/features/messages/impl/src/main/res/values-fr/translations.xml
+++ b/features/messages/impl/src/main/res/values-fr/translations.xml
@@ -5,7 +5,7 @@
- "%1$d changements dans la conversation"
-
+ - "%1$d de plus"
- "%1$d de plus"
"Appareil photo"
@@ -19,6 +19,16 @@
"Vous êtes seul dans ce chat"
"Message copié"
"Vous n‘avez pas le droit de poster dans ce salon"
+ "Autoriser les paramètres personnalisés"
+ "Activer cette option remplacera votre paramètre par défaut"
+ "Me notifier dans ce chat pour"
+ "paramètres généraux"
+ "Paramètre par défaut"
+ "Une erreur s’est produite lors du chargement des paramètres de notification."
+ "Impossible de restaurer le mode par défaut, veuillez réessayer."
+ "Impossible de régler le mode, veuillez réessayer."
+ "Tous les messages"
+ "Mentions et mots-clés uniquement"
"Afficher moins"
"Afficher plus"
"Renvoyer"
diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..7870cbc331
--- /dev/null
+++ b/features/messages/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,39 @@
+
+
+
+ - "%1$d изменение в комнате"
+ - "%1$d изменения в комнате"
+ - "%1$d изменений в комнате"
+
+ "Камера"
+ "Сделать фото"
+ "Записать видео"
+ "Вложение"
+ "Фото и Видео библиотека"
+ "Местоположение"
+ "В настоящее время история сообщений недоступна в этой комнате"
+ "Не удалось получить данные о пользователе"
+ "Хотите пригласить их снова?"
+ "Вы одни в этой комнате"
+ "Сообщение скопировано"
+ "У вас нет разрешения публиковать сообщения в этой комнате"
+ "Разрешить пользовательские настройки"
+ "Включение этого параметра отменяет настройки по умолчанию"
+ "Уведомить меня в этом чате"
+ "Вы можете изменить его в своем %1$s."
+ "Основные Настройки"
+ "Настройка по умолчанию"
+ "Произошла ошибка при загрузке настроек уведомлений."
+ "Не удалось восстановить режим по умолчанию, попробуйте еще раз."
+ "Не удалось настроить режим, попробуйте еще раз."
+ "Все сообщения"
+ "Только упоминания и ключевые слова"
+ "Показать меньше"
+ "Показать больше"
+ "Отправить снова"
+ "Не удалось отправить ваше сообщение"
+ "Добавить эмодзи"
+ "Показать меньше"
+ "Не удалось обработать медиафайл для загрузки, попробуйте еще раз."
+ "Удалить"
+
diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml
index 39b7e974be..07e62e9a4d 100644
--- a/features/messages/impl/src/main/res/values-sk/translations.xml
+++ b/features/messages/impl/src/main/res/values-sk/translations.xml
@@ -6,7 +6,7 @@
- "%1$d zmien miestnosti"
-
+ - "%1$d ďalší"
- "%1$d ďalšie"
- "%1$d ďalších"
@@ -22,6 +22,17 @@
"V tomto rozhovore ste sami"
"Správa skopírovaná"
"Nemáte povolenie uverejňovať príspevky v tejto miestnosti"
+ "Povoliť vlastné nastavenie"
+ "Zapnutím tohto nastavenia sa prepíše vaše predvolené nastavenie"
+ "Upozorniť ma v tejto konverzácii na"
+ "Môžete to zmeniť vo svojich %1$s."
+ "všeobecných nastaveniach"
+ "Predvolené nastavenie"
+ "Pri načítavaní nastavení oznámení došlo k chybe."
+ "Nepodarilo sa obnoviť predvolený režim, skúste to prosím znova."
+ "Nepodarilo sa nastaviť režim, skúste to prosím znova."
+ "Všetky správy"
+ "Iba zmienky a kľúčové slová"
"Zobraziť menej"
"Zobraziť viac"
"Odoslať znova"
diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..d7344e54ca
--- /dev/null
+++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,26 @@
+
+
+
+ - "%1$d 個聊天室變更"
+
+
+ - "還有 %1$d 個"
+
+ "照相機"
+ "拍照"
+ "錄影"
+ "附件"
+ "位置"
+ "此聊天室只有您一個人"
+ "訊息已複製"
+ "您沒有權限在此聊天室傳送訊息"
+ "全域設定"
+ "預設"
+ "無法重設為預設模式,請再試一次。"
+ "無法設定模式,請再試一次。"
+ "所有訊息"
+ "只限提及與關鍵字"
+ "重傳"
+ "無法傳送您的訊息"
+ "移除"
+
diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml
index 6303589aa2..c88beff81a 100644
--- a/features/messages/impl/src/main/res/values/localazy.xml
+++ b/features/messages/impl/src/main/res/values/localazy.xml
@@ -13,6 +13,7 @@
"Attachment"
"Photo & Video Library"
"Location"
+ "Poll"
"Message history is currently unavailable in this room"
"Could not retrieve user details"
"Would you like to invite them back?"
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
index 8e68da1d12..5fcd06a980 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
@@ -17,10 +17,9 @@
package io.element.android.features.messages
import android.net.Uri
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
-import com.google.common.collect.Iterables.skip
import com.google.common.truth.Truth.assertThat
import io.element.android.features.analytics.test.FakeAnalyticsService
import io.element.android.features.messages.fixtures.aMessageEvent
@@ -36,6 +35,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
import io.element.android.features.messages.impl.timeline.TimelinePresenter
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@@ -45,10 +45,11 @@ import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
+import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
-import io.element.android.libraries.core.meta.BuildMeta
-import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.core.mimetype.MimeTypes
+import io.element.android.libraries.designsystem.components.avatar.AvatarData
+import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.media.MediaSource
@@ -68,6 +69,8 @@ import io.element.android.libraries.mediapickers.test.FakePickerProvider
import io.element.android.libraries.mediaupload.api.MediaSender
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
import io.element.android.libraries.textcomposer.MessageComposerMode
+import io.element.android.tests.testutils.consumeItemsUntilPredicate
+import io.element.android.tests.testutils.consumeItemsUntilTimeout
import io.element.android.tests.testutils.testCoroutineDispatchers
import io.mockk.mockk
import kotlinx.coroutines.test.TestScope
@@ -81,12 +84,19 @@ class MessagesPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
- val initialState = awaitItem()
+ val initialState = consumeItemsUntilTimeout().last()
assertThat(initialState.roomId).isEqualTo(A_ROOM_ID)
+ assertThat(initialState.roomName).isEqualTo(Async.Success(""))
+ assertThat(initialState.roomAvatar).isEqualTo(Async.Success(AvatarData(id = A_ROOM_ID.value, name = "", size = AvatarSize.TimelineRoom)))
+ assertThat(initialState.userHasPermissionToSendMessage).isTrue()
+ assertThat(initialState.userHasPermissionToRedact).isFalse()
+ assertThat(initialState.hasNetworkConnection).isTrue()
+ assertThat(initialState.snackbarMessage).isNull()
+ assertThat(initialState.inviteProgress).isEqualTo(Async.Uninitialized)
+ assertThat(initialState.showReinvitePrompt).isFalse()
}
}
@@ -95,14 +105,13 @@ class MessagesPresenterTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val room = FakeMatrixRoom()
val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
assertThat(room.myReactions.count()).isEqualTo(1)
-
// No crashes when sending a reaction failed
room.givenToggleReactionResult(Result.failure(IllegalStateException("Failed to send reaction")))
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
@@ -116,10 +125,10 @@ class MessagesPresenterTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val room = FakeMatrixRoom()
val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID))
assertThat(room.myReactions.count()).isEqualTo(1)
@@ -133,10 +142,9 @@ class MessagesPresenterTest {
fun `present - handle action forward`() = runTest {
val navigator = FakeMessagesNavigator()
val presenter = createMessagePresenter(navigator = navigator)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent()))
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -149,10 +157,10 @@ class MessagesPresenterTest {
val clipboardHelper = FakeClipboardHelper()
val event = aMessageEvent()
val presenter = createMessagePresenter(clipboardHelper = clipboardHelper)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Copy, event))
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -163,13 +171,12 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent()))
-
val finalState = awaitItem()
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -179,10 +186,9 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to an event with no id does nothing`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null)))
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -194,10 +200,10 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to an image media message`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
val mediaMessage = aMessageEvent(
content = TimelineItemImageContent(
@@ -214,7 +220,6 @@ class MessagesPresenterTest {
)
)
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage))
-
val finalState = awaitItem()
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
val replyMode = finalState.composerState.mode as MessageComposerMode.Reply
@@ -226,10 +231,10 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to a video media message`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
val mediaMessage = aMessageEvent(
content = TimelineItemVideoContent(
@@ -247,7 +252,6 @@ class MessagesPresenterTest {
)
)
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage))
-
val finalState = awaitItem()
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
val replyMode = finalState.composerState.mode as MessageComposerMode.Reply
@@ -259,10 +263,10 @@ class MessagesPresenterTest {
@Test
fun `present - handle action reply to a file media message`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
val mediaMessage = aMessageEvent(
content = TimelineItemFileContent(
@@ -275,7 +279,6 @@ class MessagesPresenterTest {
)
)
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage))
-
val finalState = awaitItem()
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java)
val replyMode = finalState.composerState.mode as MessageComposerMode.Reply
@@ -287,13 +290,12 @@ class MessagesPresenterTest {
@Test
fun `present - handle action edit`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent()))
-
val finalState = awaitItem()
assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Edit::class.java)
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -305,10 +307,10 @@ class MessagesPresenterTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
val matrixRoom = FakeMatrixRoom()
val presenter = createMessagePresenter(matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Redact, aMessageEvent()))
assertThat(matrixRoom.redactEventEventIdParam).isEqualTo(AN_EVENT_ID)
@@ -320,10 +322,9 @@ class MessagesPresenterTest {
fun `present - handle action report content`() = runTest {
val navigator = FakeMessagesNavigator()
val presenter = createMessagePresenter(navigator = navigator)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent()))
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -334,13 +335,13 @@ class MessagesPresenterTest {
@Test
fun `present - handle dismiss action`() = runTest {
val presenter = createMessagePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.Dismiss)
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
+
}
}
@@ -348,10 +349,9 @@ class MessagesPresenterTest {
fun `present - handle action show developer info`() = runTest {
val navigator = FakeMessagesNavigator()
val presenter = createMessagePresenter(navigator = navigator)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
val initialState = awaitItem()
initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Developer, aMessageEvent()))
assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None)
@@ -363,23 +363,24 @@ class MessagesPresenterTest {
fun `present - shows prompt to reinvite users in DM`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 1L)
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
-
// Initially the composer doesn't have focus, so we don't show the alert
assertThat(initialState.showReinvitePrompt).isFalse()
-
// When the input field is focused we show the alert
initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true))
- val focusedState = awaitItem()
+ val focusedState = consumeItemsUntilPredicate { state ->
+ state.showReinvitePrompt
+ }.last()
assertThat(focusedState.showReinvitePrompt).isTrue()
-
// If it's dismissed then we stop showing the alert
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel))
- val dismissedState = awaitItem()
+ val dismissedState = consumeItemsUntilPredicate { state ->
+ !state.showReinvitePrompt
+ }.last()
assertThat(dismissedState.showReinvitePrompt).isFalse()
}
}
@@ -388,13 +389,12 @@ class MessagesPresenterTest {
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = false, activeMemberCount = 1L)
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.showReinvitePrompt).isFalse()
-
initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true))
val focusedState = awaitItem()
assertThat(focusedState.showReinvitePrompt).isFalse()
@@ -405,13 +405,12 @@ class MessagesPresenterTest {
fun `present - doesn't show reinvite prompt if other party is present`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 2L)
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.showReinvitePrompt).isFalse()
-
initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true))
val focusedState = awaitItem()
assertThat(focusedState.showReinvitePrompt).isFalse()
@@ -430,17 +429,14 @@ class MessagesPresenterTest {
)
)
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
- skipItems(1)
+ val initialState = consumeItemsUntilTimeout().last()
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
-
skipItems(1)
val loadingState = awaitItem()
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
-
val newState = awaitItem()
assertThat(newState.inviteProgress.isSuccess()).isTrue()
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2)
@@ -460,17 +456,14 @@ class MessagesPresenterTest {
)
)
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
- skipItems(1)
+ val initialState = consumeItemsUntilTimeout().last()
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
-
skipItems(1)
val loadingState = awaitItem()
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
-
val newState = awaitItem()
assertThat(newState.inviteProgress.isSuccess()).isTrue()
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2)
@@ -482,17 +475,14 @@ class MessagesPresenterTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID)
room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
- skipItems(1)
+ val initialState = consumeItemsUntilTimeout().last()
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
-
skipItems(1)
val loadingState = awaitItem()
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
-
val newState = awaitItem()
assertThat(newState.inviteProgress.isFailure()).isTrue()
}
@@ -511,18 +501,19 @@ class MessagesPresenterTest {
)
room.givenInviteUserResult(Result.failure(Throwable("Oops!")))
val presenter = createMessagePresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
- skipItems(1)
+ val initialState = consumeItemsUntilTimeout().last()
initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite))
- skipItems(1)
- val loadingState = awaitItem()
+ val loadingState = consumeItemsUntilPredicate { state ->
+ state.inviteProgress.isLoading()
+ }.last()
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
-
- val newState = awaitItem()
- assertThat(newState.inviteProgress.isFailure()).isTrue()
+ val failureState = consumeItemsUntilPredicate { state ->
+ state.inviteProgress.isFailure()
+ }.last()
+ assertThat(failureState.inviteProgress.isFailure()).isTrue()
}
}
@@ -531,10 +522,10 @@ class MessagesPresenterTest {
val matrixRoom = FakeMatrixRoom()
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(true))
val presenter = createMessagePresenter(matrixRoom = matrixRoom)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
-
+ skipItems(1)
assertThat(awaitItem().userHasPermissionToSendMessage).isTrue()
}
}
@@ -544,13 +535,27 @@ class MessagesPresenterTest {
val matrixRoom = FakeMatrixRoom()
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(false))
val presenter = createMessagePresenter(matrixRoom = matrixRoom)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Default value
assertThat(awaitItem().userHasPermissionToSendMessage).isTrue()
skipItems(1)
assertThat(awaitItem().userHasPermissionToSendMessage).isFalse()
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - permission to redact`() = runTest {
+ val matrixRoom = FakeMatrixRoom(canRedact = true)
+ val presenter = createMessagePresenter(matrixRoom = matrixRoom)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedact }.last()
+ assertThat(initialState.userHasPermissionToRedact).isTrue()
+ cancelAndIgnoreRemainingEvents()
}
}
@@ -580,6 +585,7 @@ class MessagesPresenterTest {
val buildMeta = aBuildMeta()
val actionListPresenter = ActionListPresenter(buildMeta = buildMeta)
val customReactionPresenter = CustomReactionPresenter()
+ val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom)
return MessagesPresenter(
room = matrixRoom,
@@ -587,6 +593,7 @@ class MessagesPresenterTest {
timelinePresenter = timelinePresenter,
actionListPresenter = actionListPresenter,
customReactionPresenter = customReactionPresenter,
+ reactionSummaryPresenter = reactionSummaryPresenter,
retrySendMenuPresenter = retrySendMenuPresenter,
networkMonitor = FakeNetworkMonitor(),
snackbarDispatcher = SnackbarDispatcher(),
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
index 0aafa68100..afef9f6730 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.messages.actionlist
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -30,7 +30,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
-import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.core.aBuildMeta
import kotlinx.collections.immutable.persistentListOf
@@ -41,7 +40,7 @@ class ActionListPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -52,12 +51,12 @@ class ActionListPresenterTest {
@Test
fun `present - compute for message from me redacted`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val messageEvent = aMessageEvent(isMine = true, content = TimelineItemRedactedContent)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -77,12 +76,12 @@ class ActionListPresenterTest {
@Test
fun `present - compute for message from others redacted`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val messageEvent = aMessageEvent(isMine = false, content = TimelineItemRedactedContent)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -102,7 +101,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for others message`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -110,7 +109,7 @@ class ActionListPresenterTest {
isMine = false,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -131,10 +130,41 @@ class ActionListPresenterTest {
}
}
+ @Test
+ fun `present - compute for others message and can redact`() = runTest {
+ val presenter = anActionListPresenter(isBuildDebuggable = true)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ val messageEvent = aMessageEvent(
+ isMine = false,
+ content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false)
+ )
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, true))
+ val successState = awaitItem()
+ assertThat(successState.target).isEqualTo(
+ ActionListState.Target.Success(
+ messageEvent,
+ persistentListOf(
+ TimelineItemAction.Reply,
+ TimelineItemAction.Forward,
+ TimelineItemAction.Copy,
+ TimelineItemAction.Developer,
+ TimelineItemAction.ReportContent,
+ TimelineItemAction.Redact,
+ )
+ )
+ )
+ initialState.eventSink.invoke(ActionListEvents.Clear)
+ assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
+ }
+ }
+
@Test
fun `present - compute for my message`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -142,7 +172,7 @@ class ActionListPresenterTest {
isMine = true,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -167,7 +197,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for a media item`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -175,7 +205,7 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemImageContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -198,7 +228,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for a state item in debug build`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = true)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -206,7 +236,7 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemStateEventContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -227,7 +257,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute for a state item in non-debuggable build`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = false)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -235,7 +265,7 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemStateEventContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -255,7 +285,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute message in non-debuggable build`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = false)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -263,7 +293,7 @@ class ActionListPresenterTest {
isMine = true,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -287,7 +317,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute message with no actions`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = false)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -300,10 +330,10 @@ class ActionListPresenterTest {
content = TimelineItemRedactedContent,
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent, false))
awaitItem().run {
assertThat(target).isEqualTo(ActionListState.Target.None)
assertThat(displayEmojiReactions).isFalse()
@@ -314,7 +344,7 @@ class ActionListPresenterTest {
@Test
fun `present - compute not sent message`() = runTest {
val presenter = anActionListPresenter(isBuildDebuggable = false)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -324,7 +354,7 @@ class ActionListPresenterTest {
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent))
+ initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false))
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
index db202569ee..fb8b3b1948 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
@@ -19,7 +19,7 @@
package io.element.android.features.messages.attachments
import android.net.Uri
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -55,7 +55,7 @@ class AttachmentsPreviewPresenterTest {
)
)
val presenter = anAttachmentsPreviewPresenter(room = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -77,7 +77,7 @@ class AttachmentsPreviewPresenterTest {
val failure = MediaPreProcessor.Failure(null)
room.givenSendMediaResult(Result.failure(failure))
val presenter = anAttachmentsPreviewPresenter(room = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -86,7 +86,7 @@ class AttachmentsPreviewPresenterTest {
val loadingState = awaitItem()
assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing)
val failureState = awaitItem()
- assertThat(failureState.sendActionState).isEqualTo((SendActionState.Failure(failure)))
+ assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
assertThat(room.sendMediaCount).isEqualTo(0)
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState)
val clearedState = awaitItem()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt
index 1357c05913..00701c0a75 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt
@@ -21,7 +21,6 @@ import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.media.local.LocalMedia
import io.element.android.features.messages.impl.media.local.MediaInfo
import io.element.android.features.messages.impl.media.local.anImageInfo
-import io.element.android.libraries.core.mimetype.MimeTypes
fun aLocalMedia(
uri: Uri,
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt
index 638c5e0556..3b1a38e16f 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt
@@ -21,6 +21,8 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseMessageFactory
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseStateFactory
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentMessageFactory
+import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentPollEndFactory
+import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentPollFactory
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentProfileChangeFactory
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentRedactedFactory
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentRoomMembershipFactory
@@ -42,6 +44,7 @@ import kotlinx.coroutines.test.TestScope
internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory {
val timelineEventFormatter = aTimelineEventFormatter()
+ val matrixClient = FakeMatrixClient()
return TimelineItemsFactory(
dispatchers = testCoroutineDispatchers(),
eventItemFactory = TimelineItemEventFactory(
@@ -49,6 +52,8 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory {
messageFactory = TimelineItemContentMessageFactory(FakeFileSizeFormatter(), FileExtensionExtractorWithoutValidation()),
redactedMessageFactory = TimelineItemContentRedactedFactory(),
stickerFactory = TimelineItemContentStickerFactory(),
+ pollFactory = TimelineItemContentPollFactory(matrixClient),
+ pollEndFactory = TimelineItemContentPollEndFactory(),
utdFactory = TimelineItemContentUTDFactory(),
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
@@ -56,7 +61,7 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory {
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory()
),
- matrixClient = FakeMatrixClient(),
+ matrixClient = matrixClient,
),
virtualItemFactory = TimelineItemVirtualFactory(
daySeparatorFactory = TimelineItemDaySeparatorFactory(
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
index 502305ab1d..983b251df7 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.messages.forward
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -24,12 +24,12 @@ import io.element.android.features.messages.impl.forward.ForwardMessagesEvents
import io.element.android.features.messages.impl.forward.ForwardMessagesPresenter
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.core.EventId
-import io.element.android.libraries.matrix.api.room.RoomSummary
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
-import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetail
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.runTest
@@ -40,7 +40,7 @@ class ForwardMessagesPresenterTests {
@Test
fun `present - initial state`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -60,7 +60,7 @@ class ForwardMessagesPresenterTests {
@Test
fun `present - toggle search active`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -76,12 +76,12 @@ class ForwardMessagesPresenterTests {
@Test
fun `present - update query`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource().apply {
+ val roomListService = FakeRoomListService().apply {
postAllRooms(listOf(RoomSummary.Filled(aRoomSummaryDetail())))
}
- val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ val client = FakeMatrixClient(roomListService = roomListService)
val presenter = aPresenter(client = client)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -96,7 +96,7 @@ class ForwardMessagesPresenterTests {
@Test
fun `present - select a room and forward successful`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -123,7 +123,7 @@ class ForwardMessagesPresenterTests {
fun `present - select a room and forward failed, then clear`() = runTest {
val room = FakeMatrixRoom()
val presenter = aPresenter(fakeMatrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -151,7 +151,7 @@ class ForwardMessagesPresenterTests {
@Test
fun `present - select and remove a room`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -166,11 +166,10 @@ class ForwardMessagesPresenterTests {
}
}
- private fun CoroutineScope.aPresenter(
+ private fun CoroutineScope.aPresenter(
eventId: EventId = AN_EVENT_ID,
fakeMatrixRoom: FakeMatrixRoom = FakeMatrixRoom(),
coroutineScope: CoroutineScope = this,
client: FakeMatrixClient = FakeMatrixClient(),
) = ForwardMessagesPresenter(eventId.value, fakeMatrixRoom, coroutineScope, client)
-
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
index 2b66d2cf9b..3c31fa49d3 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
@@ -19,7 +19,7 @@
package io.element.android.features.messages.media.viewer
import android.net.Uri
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -50,7 +50,7 @@ class MediaViewerPresenterTest {
val mediaLoader = FakeMediaLoader()
val mediaActions = FakeLocalMediaActions()
val presenter = aMediaViewerPresenter(mediaLoader, mediaActions)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
var state = awaitItem()
@@ -71,7 +71,7 @@ class MediaViewerPresenterTest {
val mediaActions = FakeLocalMediaActions()
val snackbarDispatcher = SnackbarDispatcher()
val presenter = aMediaViewerPresenter(mediaLoader, mediaActions, snackbarDispatcher)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
var state = awaitItem()
@@ -117,7 +117,7 @@ class MediaViewerPresenterTest {
val mediaLoader = FakeMediaLoader()
val mediaActions = FakeLocalMediaActions()
val presenter = aMediaViewerPresenter(mediaLoader, mediaActions)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
mediaLoader.shouldFail = true
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
index 090dd36dbe..e6b0dee0c9 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.messages.report
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -36,7 +36,7 @@ class ReportMessagePresenterTests {
@Test
fun `presenter - initial state`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -49,7 +49,7 @@ class ReportMessagePresenterTests {
@Test
fun `presenter - update reason`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -63,7 +63,7 @@ class ReportMessagePresenterTests {
@Test
fun `presenter - toggle block user`() = runTest {
val presenter = aPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -81,7 +81,7 @@ class ReportMessagePresenterTests {
fun `presenter - handle successful report and block user`() = runTest {
val room = FakeMatrixRoom()
val presenter = aPresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -98,7 +98,7 @@ class ReportMessagePresenterTests {
fun `presenter - handle successful report`() = runTest {
val room = FakeMatrixRoom()
val presenter = aPresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -115,7 +115,7 @@ class ReportMessagePresenterTests {
givenReportContentResult(Result.failure(Exception("Failed to report content")))
}
val presenter = aPresenter(matrixRoom = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
index d3fda7b881..5c75c963e5 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
@@ -19,7 +19,7 @@
package io.element.android.features.messages.textcomposer
import android.net.Uri
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test
@@ -68,7 +68,7 @@ class MessageComposerPresenterTest {
givenResult(mockk()) // Uri is not available in JVM, so the only way to have a non-null Uri is using Mockk
}
private val featureFlagService = FakeFeatureFlagService(
- mapOf(FeatureFlags.ShowMediaUploadingFlow.key to true)
+ mapOf(FeatureFlags.LocationSharing.key to true)
)
private val mediaPreProcessor = FakeMediaPreProcessor()
private val snackbarDispatcher = SnackbarDispatcher()
@@ -78,14 +78,16 @@ class MessageComposerPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.isFullScreen).isFalse()
assertThat(initialState.text).isEqualTo("")
assertThat(initialState.mode).isEqualTo(MessageComposerMode.Normal(""))
assertThat(initialState.showAttachmentSourcePicker).isFalse()
+ assertThat(initialState.canShareLocation).isTrue()
assertThat(initialState.attachmentsState).isEqualTo(AttachmentsState.None)
assertThat(initialState.isSendButtonVisible).isFalse()
}
@@ -94,9 +96,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - toggle fullscreen`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessageComposerEvents.ToggleFullScreenState)
val fullscreenState = awaitItem()
@@ -110,9 +113,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - change message`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessageComposerEvents.UpdateText(A_MESSAGE))
val withMessageState = awaitItem()
@@ -128,9 +132,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - change mode to edit`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
var state = awaitItem()
val mode = anEditMode()
state.eventSink.invoke(MessageComposerEvents.SetMode(mode))
@@ -146,9 +151,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - change mode to reply`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
var state = awaitItem()
val mode = aReplyMode()
state.eventSink.invoke(MessageComposerEvents.SetMode(mode))
@@ -163,9 +169,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - change mode to quote`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
var state = awaitItem()
val mode = aQuoteMode()
state.eventSink.invoke(MessageComposerEvents.SetMode(mode))
@@ -180,9 +187,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - send message`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink.invoke(MessageComposerEvents.UpdateText(A_MESSAGE))
val withMessageState = awaitItem()
@@ -202,9 +210,10 @@ class MessageComposerPresenterTest {
this,
fakeMatrixRoom,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.text).isEqualTo("")
val mode = anEditMode()
@@ -233,9 +242,10 @@ class MessageComposerPresenterTest {
this,
fakeMatrixRoom,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.text).isEqualTo("")
val mode = anEditMode(eventId = null, transactionId = A_TRANSACTION_ID)
@@ -264,9 +274,10 @@ class MessageComposerPresenterTest {
this,
fakeMatrixRoom,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.text).isEqualTo("")
val mode = aReplyMode()
@@ -291,9 +302,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - Open attachments menu`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
assertThat(initialState.showAttachmentSourcePicker).isEqualTo(false)
initialState.eventSink(MessageComposerEvents.AddAttachment)
@@ -304,9 +316,10 @@ class MessageComposerPresenterTest {
@Test
fun `present - Dismiss attachments menu`() = runTest {
val presenter = createPresenter(this)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.AddAttachment)
skipItems(1)
@@ -338,9 +351,10 @@ class MessageComposerPresenterTest {
)
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery)
val previewingState = awaitItem()
@@ -372,9 +386,10 @@ class MessageComposerPresenterTest {
)
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery)
val previewingState = awaitItem()
@@ -390,9 +405,10 @@ class MessageComposerPresenterTest {
givenResult(null) // Simulate a user canceling the flow
givenMimeType(MimeTypes.Images)
}
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery)
// No crashes here, otherwise it fails
@@ -410,9 +426,10 @@ class MessageComposerPresenterTest {
)
)
val presenter = createPresenter(this, room = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles)
val sendingState = awaitItem()
@@ -431,9 +448,10 @@ class MessageComposerPresenterTest {
fun `present - Take photo`() = runTest {
val room = FakeMatrixRoom()
val presenter = createPresenter(this, room = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera)
val previewingState = awaitItem()
@@ -447,9 +465,10 @@ class MessageComposerPresenterTest {
fun `present - Record video`() = runTest {
val room = FakeMatrixRoom()
val presenter = createPresenter(this, room = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera)
val previewingState = awaitItem()
@@ -464,9 +483,10 @@ class MessageComposerPresenterTest {
givenSendMediaResult(Result.failure(Exception()))
}
val presenter = createPresenter(this, room = room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
+ skipItems(1)
val initialState = awaitItem()
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles)
val sendingState = awaitItem()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
index c1d414c633..b4bb14f672 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.messages.timeline
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -24,8 +24,12 @@ import io.element.android.features.messages.fixtures.aTimelineItemsFactory
import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.TimelinePresenter
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
+import io.element.android.features.messages.impl.timeline.model.TimelineItem
+import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
+import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
+import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@@ -37,12 +41,13 @@ import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import java.util.Date
class TimelinePresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = createTimelinePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -55,7 +60,7 @@ class TimelinePresenterTest {
@Test
fun `present - load more`() = runTest {
val presenter = createTimelinePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -74,7 +79,7 @@ class TimelinePresenterTest {
@Test
fun `present - set highlighted event`() = runTest {
val presenter = createTimelinePresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -97,7 +102,7 @@ class TimelinePresenterTest {
)
)
val presenter = createTimelinePresenter(timeline)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(timeline.sendReadReceiptCount).isEqualTo(0)
@@ -114,14 +119,14 @@ class TimelinePresenterTest {
}
@Test
- fun `present - on scroll finished will not send read receipt no event is before the index`() = runTest {
+ fun `present - on scroll finished will not send read receipt if no event is before the index`() = runTest {
val timeline = FakeMatrixTimeline(
initialTimelineItems = listOf(
MatrixTimelineItem.Event(0, anEventTimelineItem())
)
)
val presenter = createTimelinePresenter(timeline)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(timeline.sendReadReceiptCount).isEqualTo(0)
@@ -145,7 +150,7 @@ class TimelinePresenterTest {
)
)
val presenter = createTimelinePresenter(timeline)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(timeline.sendReadReceiptCount).isEqualTo(0)
@@ -165,7 +170,7 @@ class TimelinePresenterTest {
fun `present - covers hasNewItems scenarios`() = runTest {
val timeline = FakeMatrixTimeline()
val presenter = createTimelinePresenter(timeline)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -188,6 +193,61 @@ class TimelinePresenterTest {
}
}
+ @Test
+ fun `present - reaction ordering`() = runTest {
+ val timeline = FakeMatrixTimeline()
+ val presenter = createTimelinePresenter(timeline)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ assertThat(initialState.hasNewItems).isFalse()
+ assertThat(initialState.timelineItems.size).isEqualTo(0)
+ val now = Date().time
+ val minuteInMilis = 60 * 1000
+ // Use index as a convenient value for timestamp
+ val (alice, bob, charlie) = aMatrixUserList().take(3).mapIndexed { i, user ->
+ ReactionSender(senderId = user.userId, timestamp = now + i * minuteInMilis)
+ }
+ val oneReaction = listOf(
+ EventReaction(
+ key = "❤️",
+ senders = listOf(alice, charlie)
+ ),
+ EventReaction(
+ key = "👍",
+ senders = listOf(alice, bob)
+ ),
+ EventReaction(
+ key = "🐶",
+ senders = listOf(charlie)
+ ),
+ )
+ timeline.updateTimelineItems {
+ listOf(MatrixTimelineItem.Event(0, anEventTimelineItem(reactions = oneReaction)))
+ }
+ skipItems(1)
+ val item = awaitItem().timelineItems.first()
+ assertThat(item).isInstanceOf(TimelineItem.Event::class.java)
+ val event = item as TimelineItem.Event
+ val reactions = event.reactionsState.reactions
+ assertThat(reactions.size).isEqualTo(3)
+
+ // Aggregated reactions are sorted by count first and then timestamp ascending(new ones tagged on the end)
+ assertThat(reactions[0].count).isEqualTo(2)
+ assertThat(reactions[0].key).isEqualTo("👍")
+ assertThat(reactions[0].senders[0].senderId).isEqualTo(bob.senderId)
+
+ assertThat(reactions[1].count).isEqualTo(2)
+ assertThat(reactions[1].key).isEqualTo("❤️")
+ assertThat(reactions[1].senders[0].senderId).isEqualTo(charlie.senderId)
+
+ assertThat(reactions[2].count).isEqualTo(1)
+ assertThat(reactions[2].key).isEqualTo("🐶")
+ assertThat(reactions[2].senders[0].senderId).isEqualTo(charlie.senderId)
+ }
+ }
+
private fun TestScope.createTimelinePresenter(
timeline: MatrixTimeline = FakeMatrixTimeline(),
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
index 237cb81d38..84628cedae 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
@@ -16,10 +16,12 @@
package io.element.android.features.messages.timeline.components.customreaction
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
+import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
+import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
import io.element.android.libraries.matrix.test.AN_EVENT_ID
@@ -32,17 +34,33 @@ class CustomReactionPresenterTests {
@Test
fun `present - handle selecting and de-selecting an event`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.selectedEventId).isNull()
- initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(AN_EVENT_ID))
+ initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(aTimelineItemEvent(eventId = AN_EVENT_ID)))
assertThat(awaitItem().selectedEventId).isEqualTo(AN_EVENT_ID)
initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(null))
assertThat(awaitItem().selectedEventId).isNull()
}
}
+
+ @Test
+ fun `present - handle selected emojis`() = runTest {
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ assertThat(initialState.selectedEventId).isNull()
+ val reactions = aTimelineItemReactions(count = 1, isHighlighted = true)
+ val key = reactions.reactions.first().key
+ initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(aTimelineItemEvent(eventId = AN_EVENT_ID, timelineItemReactions = reactions)))
+ val stateWithSelectedEmojis = awaitItem()
+ assertThat(stateWithSelectedEmojis.selectedEventId).isEqualTo(AN_EVENT_ID)
+ assertThat(stateWithSelectedEmojis.selectedEmoji).contains(key)
+ }
+ }
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt
new file mode 100644
index 0000000000..0170878cb5
--- /dev/null
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.messages.timeline.components.reactionsummary
+
+import app.cash.molecule.RecompositionMode
+import app.cash.molecule.moleculeFlow
+import app.cash.turbine.test
+import com.google.common.truth.Truth.assertThat
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
+import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
+import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction
+import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
+import io.element.android.libraries.matrix.test.AN_AVATAR_URL
+import io.element.android.libraries.matrix.test.AN_EVENT_ID
+import io.element.android.libraries.matrix.test.A_USER_ID
+import io.element.android.libraries.matrix.test.A_USER_NAME
+import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
+import io.element.android.libraries.matrix.test.room.aRoomMember
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class ReactionSummaryPresenterTests {
+ private val aggregatedReaction = anAggregatedReaction(userId = A_USER_ID, key = "👍", isHighlighted = true)
+ private val roomMember = aRoomMember(userId = A_USER_ID, avatarUrl = AN_AVATAR_URL, displayName = A_USER_NAME)
+ private val summaryEvent = ReactionSummaryEvents.ShowReactionSummary(AN_EVENT_ID, listOf(aggregatedReaction), aggregatedReaction.key)
+ private val room = FakeMatrixRoom().apply {
+ givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember)))
+ }
+ private val presenter = ReactionSummaryPresenter(room)
+
+ @Test
+ fun `present - handle showing and hiding the reaction summary`() = runTest {
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ assertThat(initialState.target).isEqualTo(null)
+
+ initialState.eventSink(summaryEvent)
+ assertThat(awaitItem().target).isNotNull()
+
+ initialState.eventSink(ReactionSummaryEvents.Clear)
+ assertThat(awaitItem().target).isNull()
+ }
+ }
+
+ @Test
+ fun `present - handle reaction summary content and avatars populated`() = runTest {
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ assertThat(initialState.target).isEqualTo(null)
+
+ initialState.eventSink(summaryEvent)
+ val reactions = awaitItem().target?.reactions
+ assertThat(reactions?.count()).isEqualTo(1)
+ assertThat(reactions?.first()?.key).isEqualTo("👍")
+ assertThat(reactions?.first()?.senders?.first()?.senderId).isEqualTo(A_USER_ID)
+ assertThat(reactions?.first()?.senders?.first()?.user?.userId).isEqualTo(A_USER_ID)
+ assertThat(reactions?.first()?.senders?.first()?.user?.avatarUrl).isEqualTo(AN_AVATAR_URL)
+ assertThat(reactions?.first()?.senders?.first()?.user?.displayName).isEqualTo(A_USER_NAME)
+ }
+ }
+
+}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
index 1e467b82af..4f4f0a0ee4 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.messages.timeline.components.retrysendmenu
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -35,7 +35,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle event selected`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -48,7 +48,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle dismiss`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -63,7 +63,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle resend with transactionId`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -79,7 +79,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle resend without transactionId`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -96,7 +96,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle resend with error`() = runTest {
room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error")))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -112,7 +112,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle remove failed message with transactionId`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -128,7 +128,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle remove failed message without transactionId`() = runTest {
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -145,7 +145,7 @@ class RetrySendMenuPresenterTests {
@Test
fun `present - handle remove failed message with error`() = runTest {
room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error")))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt
index 0e1ccbd003..ce107f76aa 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt
@@ -16,19 +16,30 @@
package io.element.android.features.messages.timeline.model
-import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
+import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction
import org.junit.Assert.assertEquals
import org.junit.Test
class AggregatedReactionTest {
@Test
fun `reaction display key is shortened`() {
- val reaction = AggregatedReaction(
- key = "1234567890123456790",
- count = 1,
- isHighlighted = false
+ val reaction = anAggregatedReaction(
+ key = "1234567890123456790",
+ count = 1
)
assertEquals("1234567890123456…", reaction.displayKey)
}
+
+ @Test
+ fun `reaction count and isHighlighted are computed correctly`() {
+ val reaction = anAggregatedReaction(
+ key = "1234567890123456790",
+ count = 3,
+ isHighlighted = true
+ )
+
+ assertEquals(3, reaction.count)
+ assertEquals(true, reaction.isHighlighted)
+ }
}
diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
index 7d01d668c9..855bf067dd 100644
--- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
+++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
@@ -42,11 +42,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@@ -86,14 +87,14 @@ private fun Indicator(modifier: Modifier = Modifier) {
.statusBarsPadding()
.padding(vertical = 6.dp),
horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.Bottom,
+ verticalAlignment = Alignment.CenterVertically,
) {
val tint = MaterialTheme.colorScheme.primary
Image(
imageVector = Icons.Outlined.WifiOff,
contentDescription = null,
colorFilter = ColorFilter.tint(tint),
- modifier = Modifier.size(16.dp),
+ modifier = Modifier.size(16.sp.toDp()),
)
Spacer(modifier = Modifier.width(8.dp))
Text(
diff --git a/features/onboarding/impl/build.gradle.kts b/features/onboarding/impl/build.gradle.kts
index 0952835d46..0f97a0ffeb 100644
--- a/features/onboarding/impl/build.gradle.kts
+++ b/features/onboarding/impl/build.gradle.kts
@@ -48,6 +48,4 @@ dependencies {
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt
index 0651d9cc2e..1adfe6bd93 100644
--- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt
+++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt
@@ -23,10 +23,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.QrCode
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.BiasAlignment
@@ -42,9 +40,8 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo
import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.components.Button
-import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.testtags.TestTags
@@ -144,46 +141,27 @@ private fun OnBoardingButtons(
}
if (state.canLoginWithQrCode) {
Button(
+ text = stringResource(id = R.string.screen_onboarding_sign_in_with_qr_code),
+ leadingIcon = IconSource.Vector(Icons.Default.QrCode),
onClick = onSignInWithQrCode,
- enabled = true,
- modifier = Modifier
- .fillMaxWidth()
- ) {
- Icon(
- imageVector = Icons.Default.QrCode, contentDescription = null,
- tint = MaterialTheme.colorScheme.onPrimary
- )
- Spacer(Modifier.width(14.dp))
- Text(
- text = stringResource(id = R.string.screen_onboarding_sign_in_with_qr_code),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ modifier = Modifier.fillMaxWidth()
+ )
}
Button(
+ text = stringResource(id = signInButtonStringRes),
onClick = onSignIn,
- enabled = true,
modifier = Modifier
.fillMaxWidth()
.testTag(TestTags.onBoardingSignIn)
- ) {
- Text(
- text = stringResource(id = signInButtonStringRes),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
if (state.canCreateAccount) {
OutlinedButton(
+ text = stringResource(id = R.string.screen_onboarding_sign_up),
onClick = onCreateAccount,
enabled = true,
modifier = Modifier
.fillMaxWidth()
- ) {
- Text(
- text = stringResource(id = R.string.screen_onboarding_sign_up),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
}
Spacer(modifier = Modifier.height(16.dp))
}
diff --git a/features/onboarding/impl/src/main/res/values-de/translations.xml b/features/onboarding/impl/src/main/res/values-de/translations.xml
index e36fa31a2b..82e20c3509 100644
--- a/features/onboarding/impl/src/main/res/values-de/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-de/translations.xml
@@ -4,7 +4,7 @@
"Mit QR-Code anmelden"
"Konto erstellen"
"Sicher kommunizieren und zusammenarbeiten"
- "Willkommen beim schnellsten Element jemals. Optimiert für Geschwindigkeit und Einfachheit."
+ "Willkommen beim schnellsten Element aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit."
"Willkommen zur %1$s. Verbessert, für Geschwindigkeit und Einfachheit."
"Sei in deinem Element"
diff --git a/features/onboarding/impl/src/main/res/values-ru/translations.xml b/features/onboarding/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..5c8c12c2b0
--- /dev/null
+++ b/features/onboarding/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,10 @@
+
+
+ "Вход в систему вручную"
+ "Войти с помощью QR-кода"
+ "Создать учетную запись"
+ "Безопасное общение и совместная работа"
+ "Добро пожаловать в самый быстрый Element. Преимущество в скорости и простоте."
+ "Добро пожаловать в %1$s. Supercharged — это скорость и простота."
+ "Будь в своей стихии"
+
diff --git a/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml b/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..5150b50c9a
--- /dev/null
+++ b/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,8 @@
+
+
+ "手動登入"
+ "使用 QR code 登入"
+ "建立帳號"
+ "歡迎使用有史以來最快的 Element。速度超快,操作簡便。"
+ "得心應手"
+
diff --git a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
index f415cd795f..d336e5b466 100644
--- a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
+++ b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.onboarding.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -27,7 +27,7 @@ class OnBoardingPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = OnBoardingPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/poll/api/build.gradle.kts b/features/poll/api/build.gradle.kts
new file mode 100644
index 0000000000..be198ba740
--- /dev/null
+++ b/features/poll/api/build.gradle.kts
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-compose-library")
+ alias(libs.plugins.ksp)
+}
+
+android {
+ namespace = "io.element.android.features.poll.api"
+}
+
+dependencies {
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.designsystem)
+ implementation(projects.libraries.uiStrings)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.androidx.constraintlayout.compose)
+ implementation(projects.libraries.matrix.api)
+
+ ksp(libs.showkase.processor)
+}
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt
new file mode 100644
index 0000000000..587c3306b1
--- /dev/null
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.api
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.BarChart
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.theme.components.Icon
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+import io.element.android.libraries.matrix.api.poll.PollKind
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+import kotlinx.collections.immutable.ImmutableList
+
+@Composable
+fun ActivePollContentView(
+ question: String,
+ answerItems: ImmutableList,
+ pollKind: PollKind,
+ onAnswerSelected: (PollAnswer) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val showResults = answerItems.any { it.isSelected }
+ Column(
+ modifier = modifier
+ .selectableGroup()
+ .fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Icon(imageVector = Icons.Default.BarChart, contentDescription = null)
+ Text(
+ text = question,
+ style = ElementTheme.typography.fontBodyLgMedium
+ )
+ }
+
+ answerItems.forEach { answerItem ->
+ PollAnswerView(
+ answerItem = answerItem,
+ onClick = { onAnswerSelected(answerItem.answer) }
+ )
+ }
+
+ val votesCount = answerItems.sumOf { it.votesCount }
+ when {
+ pollKind == PollKind.Undisclosed -> {
+ Text(
+ modifier = Modifier
+ .align(Alignment.Start)
+ .padding(start = 32.dp),
+ style = ElementTheme.typography.fontBodyXsRegular,
+ color = ElementTheme.colors.textSecondary,
+ text = stringResource(CommonStrings.common_poll_undisclosed_text),
+ )
+ }
+ showResults -> {
+ Text(
+ modifier = Modifier.align(Alignment.End),
+ style = ElementTheme.typography.fontBodyXsRegular,
+ color = ElementTheme.colors.textSecondary,
+ text = stringResource(CommonStrings.common_poll_total_votes, votesCount),
+ )
+ }
+ }
+ }
+}
+
+@DayNightPreviews
+@Composable
+internal fun ActivePollContentNoResultsPreview() = ElementPreview {
+ ActivePollContentView(
+ question = "What type of food should we have at the party?",
+ answerItems = aPollAnswerItemList(isDisclosed = false),
+ pollKind = PollKind.Undisclosed,
+ onAnswerSelected = { },
+ )
+}
+
+@DayNightPreviews
+@Composable
+internal fun ActivePollContentWithResultsPreview() = ElementPreview {
+ ActivePollContentView(
+ question = "What type of food should we have at the party?",
+ answerItems = aPollAnswerItemList(),
+ pollKind = PollKind.Disclosed,
+ onAnswerSelected = { },
+ )
+}
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt
new file mode 100644
index 0000000000..24db33ad1f
--- /dev/null
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.api
+
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+
+/**
+ * UI model for a [PollAnswer].
+ *
+ * @property answer the poll answer.
+ * @property isSelected whether the user has selected this answer.
+ * @property isDisclosed whether the votes for this answer should be disclosed.
+ * @property votesCount the number of votes for this answer.
+ * @property progress the percentage of votes for this answer.
+ */
+data class PollAnswerItem(
+ val answer: PollAnswer,
+ val isSelected: Boolean,
+ val isDisclosed: Boolean,
+ val votesCount: Int,
+ val progress: Float,
+)
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
new file mode 100644
index 0000000000..26fa6fbb71
--- /dev/null
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.api
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.res.pluralStringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
+import androidx.constraintlayout.compose.Visibility
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
+import io.element.android.libraries.designsystem.theme.components.RadioButton
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonPlurals
+
+@Suppress("DestructuringDeclarationWithTooManyEntries") // This is necessary to declare the constraints ids
+@Composable
+fun PollAnswerView(
+ answerItem: PollAnswerItem,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ ConstraintLayout(
+ modifier
+ .wrapContentHeight()
+ .fillMaxWidth()
+ .selectable(
+ selected = answerItem.isSelected,
+ onClick = onClick,
+ role = Role.RadioButton,
+ )
+ ) {
+ val (radioButton, answerText, votesText, progressBar) = createRefs()
+ RadioButton(
+ modifier = Modifier.constrainAs(radioButton) {
+ top.linkTo(answerText.top)
+ bottom.linkTo(answerText.bottom)
+ start.linkTo(parent.start)
+ end.linkTo(answerText.start)
+ },
+ selected = answerItem.isSelected,
+ onClick = null // null recommended for accessibility with screenreaders
+ )
+ Text(
+ modifier = Modifier.constrainAs(answerText) {
+ width = Dimension.fillToConstraints
+ top.linkTo(parent.top)
+ start.linkTo(radioButton.end, margin = 8.dp)
+ end.linkTo(votesText.start)
+ bottom.linkTo(progressBar.top)
+ },
+ text = answerItem.answer.text,
+ )
+ Text(
+ modifier = Modifier.constrainAs(votesText) {
+ start.linkTo(answerText.end)
+ end.linkTo(parent.end)
+ bottom.linkTo(answerText.bottom)
+ visibility = if (answerItem.isDisclosed) Visibility.Visible else Visibility.Gone
+ },
+ text = pluralStringResource(
+ id = CommonPlurals.common_poll_votes_count,
+ count = answerItem.votesCount,
+ answerItem.votesCount
+ ),
+ style = ElementTheme.typography.fontBodySmRegular,
+ color = ElementTheme.colors.textSecondary,
+ )
+ LinearProgressIndicator(
+ progress = answerItem.progress,
+ modifier = Modifier
+ .constrainAs(progressBar) {
+ start.linkTo(answerText.start)
+ end.linkTo(votesText.end)
+ top.linkTo(answerText.bottom, margin = 10.dp)
+ bottom.linkTo(parent.bottom)
+ width = Dimension.fillToConstraints
+ visibility = if (answerItem.isDisclosed) Visibility.Visible else Visibility.Gone
+
+ },
+ strokeCap = StrokeCap.Round,
+ )
+ }
+}
+
+@DayNightPreviews
+@Composable
+internal fun PollAnswerViewNoResultsPreview() = ElementPreview {
+ PollAnswerView(
+ answerItem = aPollAnswerItem(),
+ onClick = { },
+ )
+}
+
+@DayNightPreviews
+@Composable
+internal fun PollAnswerViewWithResultPreview() = ElementPreview {
+ PollAnswerView(
+ answerItem = aPollAnswerItem(isDisclosed = true),
+ onClick = { }
+ )
+}
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt
new file mode 100644
index 0000000000..062d09fd88
--- /dev/null
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.api
+
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+import kotlinx.collections.immutable.persistentListOf
+
+fun aPollAnswerItemList(isDisclosed: Boolean = true) = persistentListOf(
+ aPollAnswerItem(
+ answer = PollAnswer("option_1", "Italian \uD83C\uDDEE\uD83C\uDDF9"),
+ isDisclosed = isDisclosed,
+ votesCount = 5,
+ progress = 0.5f
+ ),
+ aPollAnswerItem(
+ answer = PollAnswer("option_2", "Chinese \uD83C\uDDE8\uD83C\uDDF3"),
+ isDisclosed = isDisclosed,
+ votesCount = 0,
+ progress = 0f
+ ),
+ aPollAnswerItem(
+ answer = PollAnswer("option_3", "Brazilian \uD83C\uDDE7\uD83C\uDDF7"),
+ isDisclosed = isDisclosed,
+ isSelected = true,
+ votesCount = 1,
+ progress = 0.1f
+ ),
+ aPollAnswerItem(isDisclosed = isDisclosed),
+)
+
+fun aPollAnswerItem(
+ answer: PollAnswer = PollAnswer(
+ "option_4",
+ "French \uD83C\uDDEB\uD83C\uDDF7 But make it a very very very long option then this should just keep expanding"
+ ),
+ isSelected: Boolean = false,
+ isDisclosed: Boolean = true,
+ votesCount: Int = 4,
+ progress: Float = 0.4f,
+) = PollAnswerItem(
+ answer = answer,
+ isSelected = isSelected,
+ isDisclosed = isDisclosed,
+ votesCount = votesCount,
+ progress = progress
+)
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt
new file mode 100644
index 0000000000..d8f2aed846
--- /dev/null
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.api
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import io.element.android.libraries.architecture.FeatureEntryPoint
+
+interface PollEntryPoint : FeatureEntryPoint {
+
+ fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
+
+ interface NodeBuilder {
+ fun callback(callback: Callback): NodeBuilder
+ fun build(): Node
+ }
+
+ interface Callback : Plugin {
+ // Add your callbacks
+ }
+}
+
diff --git a/features/poll/impl/build.gradle.kts b/features/poll/impl/build.gradle.kts
new file mode 100644
index 0000000000..626a7d0f2c
--- /dev/null
+++ b/features/poll/impl/build.gradle.kts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
+@Suppress("DSL_SCOPE_VIOLATION")
+plugins {
+ id("io.element.android-compose-library")
+ alias(libs.plugins.anvil)
+ alias(libs.plugins.ksp)
+ id("kotlin-parcelize")
+}
+
+android {
+ namespace = "io.element.android.features.poll.impl"
+}
+
+anvil {
+ generateDaggerFactories.set(true)
+}
+
+dependencies {
+ implementation(projects.anvilannotations)
+ anvil(projects.anvilcodegen)
+ api(projects.features.poll.api)
+ implementation(projects.libraries.core)
+ implementation(projects.libraries.architecture)
+ implementation(projects.libraries.matrix.api)
+ implementation(projects.libraries.matrixui)
+ implementation(projects.libraries.designsystem)
+
+ testImplementation(libs.test.junit)
+ testImplementation(libs.coroutines.test)
+ testImplementation(libs.molecule.runtime)
+ testImplementation(libs.test.truth)
+ testImplementation(libs.test.turbine)
+ testImplementation(projects.libraries.matrix.test)
+
+ ksp(libs.showkase.processor)
+}
diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt
new file mode 100644
index 0000000000..052c1bcd5f
--- /dev/null
+++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.impl
+
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.features.poll.api.PollEntryPoint
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.AppScope
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class DefaultPollEntryPoint @Inject constructor() : PollEntryPoint {
+
+ override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): PollEntryPoint.NodeBuilder {
+ val plugins = ArrayList()
+
+ return object : PollEntryPoint.NodeBuilder {
+
+ override fun callback(callback: PollEntryPoint.Callback): PollEntryPoint.NodeBuilder {
+ plugins += callback
+ return this
+ }
+
+ override fun build(): Node {
+ return parentNode.createNode(buildContext, plugins)
+ }
+ }
+ }
+}
diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt
new file mode 100644
index 0000000000..9dfeebc692
--- /dev/null
+++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.features.poll.impl
+
+import android.os.Parcelable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.composable.Children
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.bumble.appyx.navmodel.backstack.BackStack
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.architecture.BackstackNode
+import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
+import io.element.android.libraries.architecture.createNode
+import io.element.android.libraries.di.SessionScope
+import kotlinx.parcelize.Parcelize
+
+@ContributesNode(SessionScope::class)
+class PollFlowNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+) : BackstackNode(
+ backstack = BackStack(
+ initialElement = NavTarget.Root,
+ savedStateMap = buildContext.savedStateMap,
+ ),
+ buildContext = buildContext,
+ plugins = plugins,
+) {
+
+ sealed interface NavTarget : Parcelable {
+ @Parcelize
+ object Root : NavTarget
+ }
+
+ override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
+ return when (navTarget) {
+ NavTarget.Root -> {
+ createNode(buildContext)
+ }
+ }
+ }
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ Children(
+ navModel = backstack,
+ modifier = modifier,
+ transitionHandler = rememberDefaultTransitionHandler(),
+ )
+ }
+}
diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts
index f183f7f1fa..4ffca9d239 100644
--- a/features/preferences/impl/build.gradle.kts
+++ b/features/preferences/impl/build.gradle.kts
@@ -47,6 +47,7 @@ dependencies {
implementation(projects.features.ftue.api)
implementation(projects.libraries.matrixui)
implementation(projects.features.logout.api)
+ implementation(projects.services.analytics.api)
implementation(projects.services.toolbox.api)
implementation(libs.datetime)
implementation(libs.accompanist.placeholder)
@@ -68,6 +69,4 @@ dependencies {
testImplementation(projects.features.analytics.test)
testImplementation(projects.features.analytics.impl)
testImplementation(projects.tests.testutils)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt
index b7c2663b72..81075969c6 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt
@@ -50,12 +50,12 @@ fun AboutView(
@Preview
@Composable
-fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) =
+internal fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) =
+internal fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt
index 81af611716..e09e0df8f8 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt
@@ -19,17 +19,17 @@ package io.element.android.features.preferences.impl.about
import androidx.annotation.StringRes
import io.element.android.libraries.ui.strings.CommonStrings
-private const val CopyrightUrl = "https://element.io/copyright"
-private const val UsePolicyUrl = "https://element.io/acceptable-use-policy-terms"
-private const val PrivacyUrl = "https://element.io/privacy"
+private const val COPYRIGHT_URL = "https://element.io/copyright"
+private const val USE_POLICY_URL = "https://element.io/acceptable-use-policy-terms"
+private const val PRIVACY_URL = "https://element.io/privacy"
sealed class ElementLegal(
@StringRes val titleRes: Int,
val url: String,
) {
- object Copyright : ElementLegal(CommonStrings.common_copyright, CopyrightUrl)
- object AcceptableUsePolicy : ElementLegal(CommonStrings.common_acceptable_use_policy, UsePolicyUrl)
- object PrivacyPolicy : ElementLegal(CommonStrings.common_privacy_policy, PrivacyUrl)
+ object Copyright : ElementLegal(CommonStrings.common_copyright, COPYRIGHT_URL)
+ object AcceptableUsePolicy : ElementLegal(CommonStrings.common_acceptable_use_policy, USE_POLICY_URL)
+ object PrivacyPolicy : ElementLegal(CommonStrings.common_privacy_policy, PRIVACY_URL)
}
fun getAllLegals(): List {
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt
index 165406c6f5..3ee7365122 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt
@@ -46,12 +46,12 @@ fun AnalyticsSettingsView(
@Preview
@Composable
-fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) =
+internal fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) =
+internal fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt
index 1ead19154d..23ea4faf86 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt
@@ -56,6 +56,12 @@ fun DeveloperSettingsView(
RageshakePreferencesView(
state = state.rageshakeState,
)
+ PreferenceCategory(title = "Crash", showDivider = false) {
+ PreferenceText(
+ title = "Crash the app 💥",
+ onClick = { error("This crash is a test.") }
+ )
+ }
val cache = state.cacheSize
PreferenceCategory(title = "Cache", showDivider = false) {
PreferenceText(
@@ -90,12 +96,12 @@ fun FeatureListContent(
@Preview
@Composable
-fun DeveloperSettingsViewLightPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) =
+internal fun DeveloperSettingsViewLightPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) =
+internal fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
index 66ff62ee2b..0cd2e7f7db 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
@@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.user.getCurrentUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
+import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -43,6 +44,7 @@ class PreferencesRootPresenter @Inject constructor(
private val logoutPresenter: LogoutPreferencePresenter,
private val matrixClient: MatrixClient,
private val sessionVerificationService: SessionVerificationService,
+ private val analyticsService: AnalyticsService,
private val buildType: BuildType,
private val versionFormatter: VersionFormatter,
private val snackbarDispatcher: SnackbarDispatcher,
@@ -58,6 +60,7 @@ class PreferencesRootPresenter @Inject constructor(
}
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
+ val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
// Session verification status (unknown, not verified, verified)
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
@@ -72,6 +75,7 @@ class PreferencesRootPresenter @Inject constructor(
myUser = matrixUser.value,
version = versionFormatter.get(),
showCompleteVerification = sessionIsNotVerified,
+ showAnalyticsSettings = hasAnalyticsProviders,
showDeveloperSettings = showDeveloperSettings,
snackbarMessage = snackbarMessage,
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
index 2b0963c53c..540c470815 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
@@ -25,6 +25,7 @@ data class PreferencesRootState(
val myUser: MatrixUser?,
val version: String,
val showCompleteVerification: Boolean,
+ val showAnalyticsSettings: Boolean,
val showDeveloperSettings: Boolean,
val snackbarMessage: SnackbarMessage?,
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
index 9dbd54ffff..e8c148267f 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
@@ -25,6 +25,7 @@ fun aPreferencesRootState() = PreferencesRootState(
myUser = null,
version = "Version 1.1 (1)",
showCompleteVerification = true,
+ showAnalyticsSettings = true,
showDeveloperSettings = true,
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
index 01d790f8b9..556d940664 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
@@ -24,8 +24,6 @@ import androidx.compose.material.icons.outlined.DeveloperMode
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.InsertChart
import androidx.compose.material.icons.outlined.VerifiedUser
-import androidx.compose.material3.Snackbar
-import androidx.compose.material3.SnackbarHost
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -41,6 +39,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.LargeHeightPreview
import io.element.android.libraries.designsystem.theme.components.Divider
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.MatrixUserProvider
@@ -65,13 +64,7 @@ fun PreferencesRootView(
modifier = modifier,
onBackPressed = onBackPressed,
title = stringResource(id = CommonStrings.common_settings),
- snackbarHost = {
- SnackbarHost(snackbarHostState) { data ->
- Snackbar(
- snackbarData = data,
- )
- }
- }
+ snackbarHost = { SnackbarHost(snackbarHostState) }
) {
UserPreferences(state.myUser)
if (state.showCompleteVerification) {
@@ -82,11 +75,13 @@ fun PreferencesRootView(
)
Divider()
}
- PreferenceText(
- title = stringResource(id = CommonStrings.common_analytics),
- icon = Icons.Outlined.InsertChart,
- onClick = onOpenAnalytics,
- )
+ if (state.showAnalyticsSettings) {
+ PreferenceText(
+ title = stringResource(id = CommonStrings.common_analytics),
+ icon = Icons.Outlined.InsertChart,
+ onClick = onOpenAnalytics,
+ )
+ }
PreferenceText(
title = stringResource(id = CommonStrings.action_report_bug),
icon = Icons.Outlined.BugReport,
@@ -127,12 +122,12 @@ fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) {
@LargeHeightPreview
@Composable
-fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
+internal fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewLight { ContentToPreview(matrixUser) }
@LargeHeightPreview
@Composable
-fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
+internal fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewDark { ContentToPreview(matrixUser) }
@Composable
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
index 97fa158d09..4b025c10ad 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.preferences.impl.about
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -27,7 +27,7 @@ class AboutPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = AboutPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt
index 5382ad0b37..29cc25e5be 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.preferences.impl.analytics
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -33,7 +33,7 @@ class AnalyticsAnalyticsSettingsPresenterTest {
val presenter = AnalyticsSettingsPresenter(
analyticsPresenter,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
index 87a556621c..9b1bda3631 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.preferences.impl.developer
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -41,7 +41,7 @@ class DeveloperSettingsPresenterTest {
FakeClearCacheUseCase(),
rageshakePresenter
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -65,7 +65,7 @@ class DeveloperSettingsPresenterTest {
FakeClearCacheUseCase(),
rageshakePresenter,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -84,7 +84,7 @@ class DeveloperSettingsPresenterTest {
FakeClearCacheUseCase(),
rageshakePresenter,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -109,7 +109,7 @@ class DeveloperSettingsPresenterTest {
clearCacheUseCase,
rageshakePresenter,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
index f3cf23599f..580426fcfa 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
@@ -16,10 +16,11 @@
package io.element.android.features.preferences.impl.root
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
+import io.element.android.features.analytics.test.FakeAnalyticsService
import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.meta.BuildType
@@ -41,11 +42,12 @@ class PreferencesRootPresenterTest {
logoutPresenter,
matrixClient,
FakeSessionVerificationService(),
+ FakeAnalyticsService(),
BuildType.DEBUG,
FakeVersionFormatter(),
SnackbarDispatcher(),
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -61,6 +63,7 @@ class PreferencesRootPresenterTest {
)
)
assertThat(loadedState.showDeveloperSettings).isEqualTo(true)
+ assertThat(loadedState.showAnalyticsSettings).isEqualTo(false)
}
}
}
diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt
index 5d6df8bac9..04f38597ed 100644
--- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt
+++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt
@@ -17,7 +17,6 @@
package io.element.android.features.rageshake.api.detection
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.Stable
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
@Immutable
diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt
index 73e04fb5d4..3cc2a8211a 100644
--- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt
+++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt
@@ -68,12 +68,12 @@ fun RageshakePreferencesView(
@Preview
@Composable
-fun RageshakePreferencesViewLightPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) =
+internal fun RageshakePreferencesViewLightPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun RageshakePreferencesViewDarkPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) =
+internal fun RageshakePreferencesViewDarkPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt
index 0af13dcdda..99849ef1d4 100644
--- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt
+++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt
@@ -16,6 +16,8 @@
package io.element.android.features.rageshake.api.reporter
+import java.io.File
+
interface BugReporter {
/**
* Send a bug report.
@@ -43,4 +45,14 @@ interface BugReporter {
customFields: Map? = null,
listener: BugReporterListener?
)
+
+ /**
+ * Clean the log files if needed to avoid wasting disk space.
+ */
+ fun cleanLogDirectoryIfNeeded()
+
+ /**
+ * Provide the log directory.
+ */
+ fun logDirectory(): File
}
diff --git a/features/rageshake/api/src/main/res/values-ru/translations.xml b/features/rageshake/api/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..6cb17a3401
--- /dev/null
+++ b/features/rageshake/api/src/main/res/values-ru/translations.xml
@@ -0,0 +1,5 @@
+
+
+ "При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?"
+ "Кажется, вы трясли телефон. Хотите открыть экран отчета об ошибке?"
+
diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts
index 137a3bd070..464d521689 100644
--- a/features/rageshake/impl/build.gradle.kts
+++ b/features/rageshake/impl/build.gradle.kts
@@ -32,6 +32,7 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
+ implementation(projects.services.toolbox.api)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.network)
@@ -56,6 +57,4 @@ dependencies {
testImplementation(libs.test.mockk)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.features.rageshake.test)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt
index 74f3a13d13..32d45b29ff 100644
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt
@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -47,8 +48,8 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground
-import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
+import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.LogCompositions
@@ -97,6 +98,7 @@ fun BugReportView(
eventSink(BugReportEvents.SetDescription(it))
},
keyboardOptions = KeyboardOptions(
+ capitalization = KeyboardCapitalization.Sentences,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
@@ -147,14 +149,13 @@ fun BugReportView(
// Submit
PreferenceRow {
Button(
+ text = stringResource(id = CommonStrings.action_send),
onClick = { eventSink(BugReportEvents.SendBugReport) },
enabled = state.submitEnabled,
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp, bottom = 16.dp)
- ) {
- Text(text = stringResource(id = CommonStrings.action_send))
- }
+ )
}
}
@@ -176,11 +177,11 @@ fun BugReportView(
@Preview
@Composable
-fun BugReportViewLightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewLight { ContentToPreview(state) }
+internal fun BugReportViewLightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewDark { ContentToPreview(state) }
+internal fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewDark { ContentToPreview(state) }
@Composable
private fun ContentToPreview(state: BugReportState) {
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt
index a5e7edf405..b71c8af372 100644
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt
@@ -62,9 +62,9 @@ class VectorUncaughtExceptionHandler(
totalSize = info.totalMemory()
usedSize = totalSize - freeSize
}
- append("usedSize " + usedSize / 1048576L + " MB\n")
- append("freeSize " + freeSize / 1048576L + " MB\n")
- append("totalSize " + totalSize / 1048576L + " MB\n")
+ append("usedSize " + usedSize / 1_048_576L + " MB\n")
+ append("freeSize " + freeSize / 1_048_576L + " MB\n")
+ append("totalSize " + totalSize / 1_048_576L + " MB\n")
append("Thread: ")
append(thread.name)
append(", Exception: ")
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt
index eea6c1dbbf..6b5d7c9096 100644
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt
@@ -48,7 +48,7 @@ class VectorFileLogger(
}
private const val SIZE_20MB = 20 * 1024 * 1024
- private const val SIZE_50MB = 50 * 1024 * 1024
+ // private const val SIZE_50MB = 50 * 1024 * 1024
}
/*
diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
index 5695596650..a8491b5a74 100755
--- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
+++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt
@@ -18,6 +18,7 @@ package io.element.android.features.rageshake.impl.reporter
import android.content.Context
import android.os.Build
+import android.text.format.DateUtils.DAY_IN_MILLIS
import androidx.core.net.toFile
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
@@ -27,10 +28,10 @@ import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.api.reporter.ReportType
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.features.rageshake.impl.R
-import io.element.android.features.rageshake.impl.logs.VectorFileLogger
import io.element.android.libraries.androidutils.file.compressFile
import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
+import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.extensions.toOnOff
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.mimetype.MimeTypes
@@ -38,7 +39,10 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionStore
+import io.element.android.services.toolbox.api.systemclock.SystemClock
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -65,6 +69,8 @@ class DefaultBugReporter @Inject constructor(
@ApplicationContext private val context: Context,
private val screenshotHolder: ScreenshotHolder,
private val crashDataStore: CrashDataStore,
+ private val coroutineScope: CoroutineScope,
+ private val systemClock: SystemClock,
private val coroutineDispatchers: CoroutineDispatchers,
private val okHttpClient: Provider,
private val userAgentProvider: UserAgentProvider,
@@ -87,7 +93,8 @@ class DefaultBugReporter @Inject constructor(
// filenames
private const val LOG_CAT_ERROR_FILENAME = "logcatError.log"
private const val LOG_CAT_FILENAME = "logcat.log"
- private const val KEY_REQUESTS_FILENAME = "keyRequests.log"
+ private const val LOG_DIRECTORY_NAME = "logs"
+ // private const val KEY_REQUESTS_FILENAME = "keyRequests.log"
private const val BUFFER_SIZE = 1024 * 1024 * 50
}
@@ -103,7 +110,7 @@ class DefaultBugReporter @Inject constructor(
.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java))
*/
- private val LOGCAT_CMD_ERROR = arrayOf(
+ private val logcatCommandError = arrayOf(
"logcat", // /< Run 'logcat' command
"-d", // /< Dump the log rather than continue outputting it
"-v", // formatting
@@ -114,7 +121,7 @@ class DefaultBugReporter @Inject constructor(
"*:S" // /< Everything else silent, so don't pick it..
)
- private val LOGCAT_CMD_DEBUG = arrayOf("logcat", "-d", "-v", "threadtime", "*:*")
+ private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*")
/**
* Send a bug report.
@@ -158,9 +165,8 @@ class DefaultBugReporter @Inject constructor(
val gzippedFiles = ArrayList()
- val vectorFileLogger = VectorFileLogger.getFromTimber()
- if (withDevicesLogs && vectorFileLogger != null) {
- val files = vectorFileLogger.getLogFiles()
+ if (withDevicesLogs) {
+ val files = getLogFiles()
files.mapNotNullTo(gzippedFiles) { f ->
if (!mIsCancelled) {
compressFile(f)
@@ -168,6 +174,7 @@ class DefaultBugReporter @Inject constructor(
null
}
}
+ files.deleteAllExceptMostRecent()
}
if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) {
@@ -458,6 +465,54 @@ class DefaultBugReporter @Inject constructor(
)
}
+ override fun logDirectory(): File {
+ return File(context.cacheDir, LOG_DIRECTORY_NAME)
+ }
+
+ override fun cleanLogDirectoryIfNeeded() {
+ coroutineScope.launch(coroutineDispatchers.io) {
+ // delete the log files older than 1 day, except the most recent one
+ deleteOldLogFiles(systemClock.epochMillis() - DAY_IN_MILLIS)
+ }
+ }
+
+ /**
+ * @return the files on the log directory.
+ */
+ private fun getLogFiles(): List {
+ return tryOrNull(
+ onError = { Timber.e(it, "## getLogFiles() failed") }
+ ) {
+ val logDirectory = logDirectory()
+ logDirectory.listFiles()?.toList()
+ }.orEmpty()
+ }
+
+ /**
+ * Delete the log files older than the given time except the most recent one.
+ * @param time the time in ms
+ */
+ private fun deleteOldLogFiles(time: Long) {
+ val logFiles = getLogFiles()
+ val oldLogFiles = logFiles.filter { it.lastModified() < time }
+ oldLogFiles.deleteAllExceptMostRecent()
+ }
+
+ /**
+ * Delete all the log files except the most recent one.
+ *
+ */
+ private fun List.deleteAllExceptMostRecent() {
+ if (size > 1) {
+ val mostRecentFile = maxByOrNull { it.lastModified() }
+ forEach { file ->
+ if (file != mostRecentFile) {
+ file.safeDelete()
+ }
+ }
+ }
+ }
+
// ==============================================================================================================
// Logcat management
// ==============================================================================================================
@@ -485,6 +540,10 @@ class DefaultBugReporter @Inject constructor(
Timber.e(error, "## saveLogCat() : fail to write logcat OOM")
} catch (e: Exception) {
Timber.e(e, "## saveLogCat() : fail to write logcat")
+ } finally {
+ if (logCatErrFile.exists()) {
+ logCatErrFile.safeDelete()
+ }
}
return null
@@ -500,7 +559,7 @@ class DefaultBugReporter @Inject constructor(
val logcatProc: Process
try {
- logcatProc = Runtime.getRuntime().exec(if (isErrorLogCat) LOGCAT_CMD_ERROR else LOGCAT_CMD_DEBUG)
+ logcatProc = Runtime.getRuntime().exec(if (isErrorLogCat) logcatCommandError else logcatCommandDebug)
} catch (e1: IOException) {
return
}
diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml
index a24318545d..b316d8b45e 100644
--- a/features/rageshake/impl/src/main/res/values-de/translations.xml
+++ b/features/rageshake/impl/src/main/res/values-de/translations.xml
@@ -1,14 +1,14 @@
"Bildschirmfoto anhängen"
- "Sie können mich kontaktieren, wenn Sie weitere Fragen haben"
+ "Ihr könnt mich kontaktieren, wenn ihr weitere Fragen habt"
"Kontaktiere mich"
"Bildschirmfoto bearbeiten"
"Beschreibe bitte den Fehler. Was hast du gemacht? Was hätte passieren sollen? Was ist passiert? Bitte beschreibe alles mit so vielen Details wie möglich."
"Beschreibe den Fehler…"
- "Wenn möglich, verfassen Sie die Beschreibung bitte auf Englisch."
+ "Wenn möglich, verfasse die Beschreibung bitte auf Englisch."
"Absturzprotokolle senden"
- "Senden Sie Protokolle, um zu helfen"
+ "Logs zulassen"
"Bildschirmfoto senden"
"Um zu überprüfen, ob alles wie vorgesehen funktioniert, werden Protokolle mit deiner Nachricht gesendet. Diese werden privat sein. Um nur Ihre Nachricht zu senden, schalte diese Einstellung aus."
"%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?"
diff --git a/features/rageshake/impl/src/main/res/values-fr/translations.xml b/features/rageshake/impl/src/main/res/values-fr/translations.xml
index bf6ad2d215..53b95af6be 100644
--- a/features/rageshake/impl/src/main/res/values-fr/translations.xml
+++ b/features/rageshake/impl/src/main/res/values-fr/translations.xml
@@ -1,7 +1,7 @@
"Joindre une capture d\'écran"
- "Vous pouvez me contacter si vous avez des questions complémentaires"
+ "Vous pouvez me contacter si vous avez des questions complémentaires."
"Me contacter"
"Modifier la capture d\'écran"
"S\'il vous plait, veuillez décrire le bogue. Qu\'avez-vous fait ? À quoi vous attendiez-vous ? Que s\'est-il réellement passé. Veuillez ajouter le plus de détails possible."
diff --git a/features/rageshake/impl/src/main/res/values-ru/translations.xml b/features/rageshake/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..8f05a3148d
--- /dev/null
+++ b/features/rageshake/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,15 @@
+
+
+ "Приложить снимок экрана"
+ "Вы можете связаться со мной, если у Вас возникнут какие-либо дополнительные вопросы."
+ "Связаться со мной"
+ "Редактировать снимок экрана"
+ "Пожалуйста, опишите ошибку. Что вы сделали? Что вы ожидали, что произойдет? Что произошло на самом деле. Пожалуйста, опишите все как можно подробнее."
+ "Опишите ошибку…"
+ "Если возможно, пожалуйста, напишите описание на английском языке."
+ "Отправка журналов сбоев"
+ "Разрешить ведение журналов"
+ "Отправить снимок экрана"
+ "Чтобы убедиться, что все работает правильно, в сообщение будут включены журналы. Чтобы отправить сообщение без журналов, отключите эту настройку."
+ "При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?"
+
diff --git a/features/rageshake/impl/src/main/res/values-sk/translations.xml b/features/rageshake/impl/src/main/res/values-sk/translations.xml
index cb530d1712..51222367a6 100644
--- a/features/rageshake/impl/src/main/res/values-sk/translations.xml
+++ b/features/rageshake/impl/src/main/res/values-sk/translations.xml
@@ -1,7 +1,7 @@
"Priložiť snímku obrazovky"
- "V prípade ďalších otázok ma môžete kontaktovať"
+ "V prípade ďalších otázok ma môžete kontaktovať."
"Kontaktujte ma"
"Upraviť snímku obrazovky"
"Popíšte prosím chybu. Čo ste urobili? Čo ste očakávali, že sa stane? Čo sa skutočne stalo. Prosím, uveďte čo najviac podrobností."
diff --git a/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml b/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..6e9eaabed3
--- /dev/null
+++ b/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,7 @@
+
+
+ "附上螢幕截圖"
+ "聯絡我"
+ "編輯螢幕截圖"
+ "傳送螢幕截圖"
+
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt
index 9b868a637d..c0418783dd 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.rageshake.impl.bugreport
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -41,7 +41,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -62,7 +62,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -81,7 +81,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -100,7 +100,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -120,7 +120,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -139,7 +139,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -161,7 +161,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -186,7 +186,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -215,7 +215,7 @@ class BugReportPresenterTest {
FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI),
this,
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt
index ac8940a1ac..82edaf563d 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt
@@ -21,6 +21,7 @@ import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.api.reporter.ReportType
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
import kotlinx.coroutines.delay
+import java.io.File
class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Success) : BugReporter {
override suspend fun sendBugReport(
@@ -55,6 +56,14 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes
delay(100)
listener?.onUploadSucceed(null)
}
+
+ override fun cleanLogDirectoryIfNeeded() {
+ // No op
+ }
+
+ override fun logDirectory(): File {
+ return File("fake")
+ }
}
enum class FakeBugReporterMode {
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt
index 2d9834607f..b8b8c4b6d0 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.rageshake.impl.crash.ui
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -33,7 +33,7 @@ class CrashDetectionPresenterTest {
val presenter = DefaultCrashDetectionPresenter(
FakeCrashDataStore()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -46,7 +46,7 @@ class CrashDetectionPresenterTest {
val presenter = DefaultCrashDetectionPresenter(
FakeCrashDataStore(appHasCrashed = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -61,7 +61,7 @@ class CrashDetectionPresenterTest {
val presenter = DefaultCrashDetectionPresenter(
FakeCrashDataStore(appHasCrashed = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -77,7 +77,7 @@ class CrashDetectionPresenterTest {
val presenter = DefaultCrashDetectionPresenter(
FakeCrashDataStore(appHasCrashed = true, crashData = A_CRASH_DATA)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt
index eb49eb450e..02a0fc0794 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt
@@ -17,7 +17,7 @@
package io.element.android.features.rageshake.impl.detection
import android.graphics.Bitmap
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -59,7 +59,7 @@ class RageshakeDetectionPresenterTest {
rageshakeDataStore = rageshakeDataStore,
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -83,7 +83,7 @@ class RageshakeDetectionPresenterTest {
rageshakeDataStore = rageshakeDataStore,
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -108,7 +108,7 @@ class RageshakeDetectionPresenterTest {
rageshakeDataStore = rageshakeDataStore,
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -142,7 +142,7 @@ class RageshakeDetectionPresenterTest {
rageshakeDataStore = rageshakeDataStore,
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -176,7 +176,7 @@ class RageshakeDetectionPresenterTest {
rageshakeDataStore = rageshakeDataStore,
)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt
index b01ce22645..56759c360c 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.rageshake.impl.preferences
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -34,7 +34,7 @@ class RageshakePreferencesPresenterTest {
FakeRageShake(isAvailableValue = true),
FakeRageshakeDataStore(isEnabled = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -50,7 +50,7 @@ class RageshakePreferencesPresenterTest {
FakeRageShake(isAvailableValue = false),
FakeRageshakeDataStore(isEnabled = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -66,7 +66,7 @@ class RageshakePreferencesPresenterTest {
FakeRageShake(isAvailableValue = true),
FakeRageshakeDataStore(isEnabled = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -85,7 +85,7 @@ class RageshakePreferencesPresenterTest {
FakeRageShake(isAvailableValue = true),
FakeRageshakeDataStore(isEnabled = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
index 9aa8ea41c3..51754ca6de 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
@@ -17,18 +17,15 @@
package io.element.android.features.roomdetails.impl
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -50,6 +47,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.leaveroom.api.LeaveRoomView
@@ -69,7 +67,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.LargeHeightPreview
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
-import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -197,7 +194,7 @@ internal fun RoomDetailsTopBar(
onDismissRequest = { showMenu = false },
) {
DropdownMenuItem(
- text = { DropdownMenuItemText(stringResource(id = CommonStrings.action_edit)) },
+ text = { Text(stringResource(id = CommonStrings.action_edit)) },
onClick = {
// Explicitly close the menu before handling the action, as otherwise it stays open during the
// transition and renders really badly.
@@ -226,18 +223,30 @@ internal fun RoomHeaderSection(
roomAlias: String?,
modifier: Modifier = Modifier
) {
- Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
- Box(modifier = Modifier.size(70.dp)) {
- Avatar(
- avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader),
- modifier = Modifier.fillMaxSize()
- )
- }
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Avatar(
+ avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader),
+ modifier = Modifier.size(70.dp)
+ )
Spacer(modifier = Modifier.height(24.dp))
- Text(roomName, style = ElementTheme.typography.fontHeadingLgBold)
+ Text(
+ text = roomName,
+ style = ElementTheme.typography.fontHeadingLgBold,
+ textAlign = TextAlign.Center,
+ )
if (roomAlias != null) {
Spacer(modifier = Modifier.height(6.dp))
- Text(roomAlias, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary)
+ Text(
+ text = roomAlias,
+ style = ElementTheme.typography.fontBodyLgRegular,
+ color = MaterialTheme.colorScheme.secondary,
+ textAlign = TextAlign.Center,
+ )
}
Spacer(Modifier.height(32.dp))
}
@@ -322,12 +331,12 @@ internal fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = M
@LargeHeightPreview
@Composable
-fun RoomDetailsLightPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) =
+internal fun RoomDetailsLightPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) =
ElementPreviewLight { ContentToPreview(state) }
@LargeHeightPreview
@Composable
-fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) =
+internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
index 029f5ac5df..cd0cbf878e 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
@@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetValue
@@ -52,6 +53,7 @@ import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -66,7 +68,6 @@ import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -113,17 +114,13 @@ fun RoomDetailsEditView(
navigationIcon = { BackButton(onClick = onBackPressed) },
actions = {
TextButton(
+ text = stringResource(CommonStrings.action_save),
enabled = state.saveButtonEnabled,
onClick = {
focusManager.clearFocus()
state.eventSink(RoomDetailsEditEvents.Save)
},
- ) {
- Text(
- text = stringResource(CommonStrings.action_save),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
}
)
},
@@ -164,6 +161,9 @@ fun RoomDetailsEditView(
placeholder = stringResource(CommonStrings.common_topic_placeholder),
maxLines = 10,
onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(it)) },
+ keyboardOptions = KeyboardOptions(
+ capitalization = KeyboardCapitalization.Sentences,
+ ),
)
} else {
LabelledReadOnlyField(
@@ -287,12 +287,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
@Preview
@Composable
-fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) =
+internal fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) =
+internal fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt
index 00cb04b118..cc0886c3af 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt
@@ -93,7 +93,7 @@ class RoomInviteMembersPresenter @Inject constructor(
value = if (value.contains(user)) {
value.filterNot { it == user }
} else {
- (value + user)
+ value + user
}.toImmutableList()
}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt
index 555f04af20..8f01569f01 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt
@@ -128,10 +128,8 @@ fun RoomInviteMembersTopBar(
navigationIcon = { BackButton(onClick = onBackPressed) },
actions = {
TextButton(
+ text = stringResource(CommonStrings.action_send),
onClick = onSendPressed,
- content = {
- Text(stringResource(CommonStrings.action_send))
- },
enabled = canSend,
)
}
@@ -220,12 +218,12 @@ private fun RoomInviteMembersSearchBar(
@Preview
@Composable
-fun RoomInviteMembersLightPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) =
+internal fun RoomInviteMembersLightPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun RoomInviteMembersDarkPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) =
+internal fun RoomInviteMembersDarkPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt
index 0787563aed..49ae479d40 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt
@@ -18,7 +18,6 @@ package io.element.android.features.roomdetails.impl.members
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -31,7 +30,6 @@ import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.room.MatrixRoom
-import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
import kotlinx.collections.immutable.toImmutableList
@@ -101,6 +99,5 @@ class RoomMemberListPresenter @Inject constructor(
},
)
}
-
}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt
index 3bfde66c06..b0bac12d4e 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt
@@ -45,7 +45,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Scaffold
@@ -212,14 +211,9 @@ private fun RoomMemberListTopBar(
actions = {
if (canInvite) {
TextButton(
- modifier = Modifier.padding(horizontal = 8.dp),
+ text = stringResource(CommonStrings.action_invite),
onClick = onInvitePressed,
- ) {
- Text(
- text = stringResource(CommonStrings.action_invite),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
}
}
)
@@ -257,12 +251,12 @@ private fun RoomMemberSearchBar(
@Preview
@Composable
-fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) =
+internal fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) =
+internal fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
index 72b9d4c20c..84dd9319cf 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
@@ -39,6 +39,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs
@@ -118,10 +119,16 @@ internal fun RoomMemberHeaderSection(
}
Spacer(modifier = Modifier.height(24.dp))
if (userName != null) {
- Text(userName, style = ElementTheme.typography.fontHeadingLgBold)
+ Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold)
Spacer(modifier = Modifier.height(6.dp))
}
- Text(userId, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary)
+ Text(
+ text = userId,
+ style = ElementTheme.typography.fontBodyLgRegular,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
+ textAlign = TextAlign.Center,
+ )
Spacer(Modifier.height(40.dp))
}
}
@@ -146,12 +153,12 @@ internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier =
@LargeHeightPreview
@Composable
-fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
+internal fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
ElementPreviewLight { ContentToPreview(state) }
@LargeHeightPreview
@Composable
-fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
+internal fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml
index ee34445805..2696cc99ea 100644
--- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml
@@ -1,7 +1,7 @@
- - "1 membre"
+ - "%1$d membre"
- "%1$d membres"
"Définir un sujet"
@@ -12,17 +12,22 @@
"Impossible de mettre à jour le salon"
"Les messages sont sécurisés par des cadenas numériques. Seuls vous et les destinataires possédez les clés uniques pour les déverrouiller."
"Chiffrement des messages activé"
+ "Une erreur s’est produite lors du chargement des paramètres de notification."
+ "Impossible de désactiver les notifications de cette salle, veuillez réessayer."
+ "Impossible de réactiver les notifications de cette salle, veuillez réessayer."
"Inviter des personnes"
+ "Personnalisé"
+ "Par défaut"
"Notifications"
"Nom du salon"
"Partager le salon"
"Mise à jour du salon…"
"En attente"
"Bloquer"
- "Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez annuler cette action à tout moment."
+ "Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez les débloquer à tout moment."
"Bloquer l\'utilisateur"
"Débloquer"
- "Lorsque vous débloquez l\'utilisateur, vous pourrez à nouveau voir tous leur messages."
+ "Vous pourrez à nouveau voir tous leurs messages."
"Débloquer l\'utilisateur"
"Quitter le salon"
"Personnes"
diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..4d2664ab30
--- /dev/null
+++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,38 @@
+
+
+
+ - "%1$d пользователь"
+ - "%1$d пользователя"
+ - "%1$d пользователей"
+
+ "Добавить тему"
+ "Уже зарегистрирован"
+ "Уже приглашены"
+ "Редактировать комнату"
+ "Произошла неизвестная ошибка, и информацию нельзя было изменить."
+ "Не удалось обновить комнату"
+ "Сообщения зашифрованы. Только у вас и у получателей есть уникальные ключи для их разблокировки."
+ "Шифрование сообщений включено"
+ "При загрузке настроек уведомлений произошла ошибка."
+ "Не удалось отключить звук в этой комнате, попробуйте еще раз."
+ "Не удалось включить звук в эту комнату, попробуйте еще раз."
+ "Пригласить участника"
+ "Пользовательский"
+ "По умолчанию"
+ "Уведомления"
+ "Название комнаты"
+ "Поделиться комнатой"
+ "Обновление комнаты…"
+ "В ожидании"
+ "Участники комнаты"
+ "Заблокировать"
+ "Заблокированные пользователи не смогут отправлять вам сообщения, а все их сообщения будут скрыты. Вы можете разблокировать их в любое время."
+ "Заблокировать пользователя"
+ "Разблокировать"
+ "Вы снова сможете увидеть все сообщения."
+ "Разблокировать пользователя"
+ "Покинуть комнату"
+ "Пользователи"
+ "Безопасность"
+ "Тема"
+
diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..cbac73f938
--- /dev/null
+++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,27 @@
+
+
+
+ - "%1$d 位夥伴"
+
+ "新增主題"
+ "已是成員"
+ "已邀請"
+ "編輯聊天室"
+ "訊息已加密"
+ "邀請夥伴"
+ "自訂"
+ "預設"
+ "通知"
+ "聊天室名稱"
+ "分享聊天室"
+ "正在更新聊天室…"
+ "待定"
+ "聊天室成員"
+ "封鎖"
+ "封鎖使用者"
+ "解除封鎖"
+ "解除封鎖使用者"
+ "離開聊天室"
+ "夥伴"
+ "主題"
+
diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml
index 158ba386b4..b1f67dab1e 100644
--- a/features/roomdetails/impl/src/main/res/values/localazy.xml
+++ b/features/roomdetails/impl/src/main/res/values/localazy.xml
@@ -1,7 +1,7 @@
- - "1 person"
+ - "%1$d person"
- "%1$d people"
"Add topic"
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
index ccd1476c3a..08d6a58535 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.roomdetails
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -60,7 +60,7 @@ class RoomDetailsPresenterTests {
fun `present - initial state is created from room info`() = runTest {
val room = aMatrixRoom()
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -79,7 +79,7 @@ class RoomDetailsPresenterTests {
fun `present - initial state with no room name`() = runTest {
val room = aMatrixRoom(name = null)
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -101,7 +101,7 @@ class RoomDetailsPresenterTests {
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -117,7 +117,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(true))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -135,7 +135,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(awaitItem().canInvite).isFalse()
@@ -148,7 +148,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.failure(Throwable("Whoops")))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(awaitItem().canInvite).isFalse()
@@ -164,7 +164,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -193,7 +193,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -222,7 +222,7 @@ class RoomDetailsPresenterTests {
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -243,7 +243,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -264,7 +264,7 @@ class RoomDetailsPresenterTests {
givenCanInviteResult(Result.success(false))
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false, and no further events
@@ -280,7 +280,7 @@ class RoomDetailsPresenterTests {
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// The initial state is "hidden" and no further state changes happen
@@ -296,7 +296,7 @@ class RoomDetailsPresenterTests {
}
val presenter = aRoomDetailsPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Ignore the initial state
@@ -314,7 +314,7 @@ class RoomDetailsPresenterTests {
val leaveRoomPresenter = LeaveRoomPresenterFake()
val room = aMatrixRoom()
val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
index 20d253f3fb..e43703e235 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
@@ -17,7 +17,7 @@
package io.element.android.features.roomdetails.edit
import android.net.Uri
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -82,7 +82,7 @@ class RoomDetailsEditPresenterTest {
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL)
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -109,7 +109,7 @@ class RoomDetailsEditPresenterTest {
}
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -135,7 +135,7 @@ class RoomDetailsEditPresenterTest {
}
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -161,7 +161,7 @@ class RoomDetailsEditPresenterTest {
}
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Initially false
@@ -183,7 +183,7 @@ class RoomDetailsEditPresenterTest {
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -229,7 +229,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -250,7 +250,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -271,7 +271,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -323,7 +323,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -373,7 +373,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -398,7 +398,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -422,7 +422,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -445,7 +445,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -470,7 +470,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -495,7 +495,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -561,7 +561,7 @@ class RoomDetailsEditPresenterTest {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -580,7 +580,7 @@ class RoomDetailsEditPresenterTest {
private suspend fun saveAndAssertFailure(room: MatrixRoom, event: RoomDetailsEditEvents) {
val presenter = aRoomDetailsEditPresenter(room)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt
index 8600cefeac..ede7342882 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.roomdetails.impl.invite
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -52,7 +52,7 @@ internal class RoomInviteMembersPresenterTest {
coroutineDispatchers = testCoroutineDispatchers()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -74,7 +74,7 @@ internal class RoomInviteMembersPresenterTest {
coroutineDispatchers = testCoroutineDispatchers()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -95,7 +95,7 @@ internal class RoomInviteMembersPresenterTest {
roomMemberListDataSource = createDataSource(FakeMatrixRoom()),
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -121,7 +121,7 @@ internal class RoomInviteMembersPresenterTest {
roomMemberListDataSource = createDataSource(FakeMatrixRoom()),
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -173,7 +173,7 @@ internal class RoomInviteMembersPresenterTest {
),
coroutineDispatchers = coroutineDispatchers
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -232,7 +232,7 @@ internal class RoomInviteMembersPresenterTest {
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -270,7 +270,7 @@ internal class RoomInviteMembersPresenterTest {
coroutineDispatchers = testCoroutineDispatchers()
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -298,7 +298,7 @@ internal class RoomInviteMembersPresenterTest {
roomMemberListDataSource = createDataSource(FakeMatrixRoom()),
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -339,7 +339,7 @@ internal class RoomInviteMembersPresenterTest {
roomMemberListDataSource = createDataSource(FakeMatrixRoom()),
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt
index c3a79481e6..9de035d017 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.roomdetails.members
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -44,7 +44,7 @@ class RoomMemberListPresenterTests {
@Test
fun `search is done automatically on start, but is async`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -52,7 +52,6 @@ class RoomMemberListPresenterTests {
Truth.assertThat(initialState.searchQuery).isEmpty()
Truth.assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.NotSearching::class.java)
Truth.assertThat(initialState.isSearchActive).isFalse()
-
val loadedState = awaitItem()
Truth.assertThat(loadedState.roomMembers).isInstanceOf(Async.Success::class.java)
Truth.assertThat((loadedState.roomMembers as Async.Success).data.invited).isEqualTo(listOf(aVictor(), aWalter()))
@@ -63,32 +62,30 @@ class RoomMemberListPresenterTests {
@Test
fun `open search`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
+ skipItems(1)
val loadedState = awaitItem()
-
loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true))
-
val searchActiveState = awaitItem()
- Truth.assertThat((searchActiveState.isSearchActive)).isTrue()
+ Truth.assertThat(searchActiveState.isSearchActive).isTrue()
}
}
@Test
fun `search for something which is not found`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
+ skipItems(1)
val loadedState = awaitItem()
loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true))
val searchActiveState = awaitItem()
- loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something"))
+ searchActiveState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something"))
val searchQueryUpdatedState = awaitItem()
- Truth.assertThat((searchQueryUpdatedState.searchQuery)).isEqualTo("something")
+ Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("something")
val searchSearchResultDelivered = awaitItem()
Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.NoResults::class.java)
}
@@ -97,21 +94,20 @@ class RoomMemberListPresenterTests {
@Test
fun `search for something which is found`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = awaitItem()
+ skipItems(1)
val loadedState = awaitItem()
loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true))
val searchActiveState = awaitItem()
- loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice"))
+ searchActiveState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice"))
val searchQueryUpdatedState = awaitItem()
- Truth.assertThat((searchQueryUpdatedState.searchQuery)).isEqualTo("Alice")
+ Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("Alice")
val searchSearchResultDelivered = awaitItem()
- Truth.assertThat((searchSearchResultDelivered.searchResults)).isInstanceOf(SearchBarResultState.Results::class.java)
+ Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.Results::class.java)
Truth.assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.joined.first().displayName)
.isEqualTo("Alice")
-
}
}
@@ -122,7 +118,7 @@ class RoomMemberListPresenterTests {
givenCanInviteResult(Result.success(true))
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -138,7 +134,7 @@ class RoomMemberListPresenterTests {
givenCanInviteResult(Result.success(false))
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -154,7 +150,7 @@ class RoomMemberListPresenterTests {
givenCanInviteResult(Result.failure(Throwable("Eek")))
}
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt
index 94b940bb17..71df3ad633 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.roomdetails.members.details
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -45,7 +45,7 @@ class RoomMemberDetailsPresenterTests {
givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember)))
}
val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -69,7 +69,7 @@ class RoomMemberDetailsPresenterTests {
givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember)))
}
val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -89,7 +89,7 @@ class RoomMemberDetailsPresenterTests {
givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember)))
}
val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -105,7 +105,7 @@ class RoomMemberDetailsPresenterTests {
val room = aMatrixRoom()
val roomMember = aRoomMember()
val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -126,7 +126,7 @@ class RoomMemberDetailsPresenterTests {
val room = aMatrixRoom()
val roomMember = aRoomMember()
val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -147,7 +147,7 @@ class RoomMemberDetailsPresenterTests {
val matrixClient = FakeMatrixClient()
matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE))
val presenter = RoomMemberDetailsPresenter(matrixClient, room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -166,7 +166,7 @@ class RoomMemberDetailsPresenterTests {
val room = aMatrixRoom()
val roomMember = aRoomMember()
val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts
index 302e54fe09..f5a08ba860 100644
--- a/features/roomlist/impl/build.gradle.kts
+++ b/features/roomlist/impl/build.gradle.kts
@@ -71,6 +71,4 @@ dependencies {
testImplementation(projects.features.networkmonitor.test)
testImplementation(projects.tests.testutils)
testImplementation(projects.features.leaveroom.fake)
-
- androidTestImplementation(libs.test.junitext)
}
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt
index 740c14aa8d..700235f3e2 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt
@@ -30,7 +30,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.VectorIcons
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
index 21f946b3fd..dbef9c799b 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
@@ -44,7 +44,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
-private const val extendedRangeSize = 40
+private const val EXTENDED_RANGE_SIZE = 40
class RoomListPresenter @Inject constructor(
private val client: MatrixClient,
@@ -130,11 +130,11 @@ class RoomListPresenter @Inject constructor(
private fun updateVisibleRange(range: IntRange) {
if (range.isEmpty()) return
- val midExtendedRangeSize = extendedRangeSize / 2
+ val midExtendedRangeSize = EXTENDED_RANGE_SIZE / 2
val extendedRangeStart = (range.first - midExtendedRangeSize).coerceAtLeast(0)
// Safe to give bigger size than room list
val extendedRangeEnd = range.last + midExtendedRangeSize
val extendedRange = IntRange(extendedRangeStart, extendedRangeEnd)
- client.roomSummaryDataSource.updateAllRoomsVisibleRange(extendedRange)
+ client.roomListService.updateAllRoomsVisibleRange(extendedRange)
}
}
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
index 657648df42..02a72306dc 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
@@ -28,8 +28,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Snackbar
-import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
@@ -59,6 +57,7 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.LogCompositions
+import io.element.android.libraries.designsystem.utils.SnackbarHost
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.designsystem.R as DrawableR
@@ -227,13 +226,7 @@ fun RoomListContent(
)
}
},
- snackbarHost = {
- SnackbarHost(snackbarHostState) { data ->
- Snackbar(
- snackbarData = data,
- )
- }
- },
+ snackbarHost = { SnackbarHost(snackbarHostState) },
)
}
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
index 16eea28f20..9d6b54366a 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt
@@ -19,7 +19,6 @@ package io.element.android.features.roomlist.impl.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -37,7 +36,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.features.roomlist.impl.R
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
+import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
@@ -83,15 +82,11 @@ internal fun RequestVerificationHeader(
)
Spacer(modifier = Modifier.height(12.dp))
Button(
+ text = stringResource(CommonStrings.action_continue),
+ size = ButtonSize.Medium,
modifier = Modifier.fillMaxWidth(),
- contentPadding = PaddingValues(horizontal = 20.dp, vertical = 7.dp),
onClick = onVerifyClicked,
- ) {
- Text(
- stringResource(CommonStrings.action_continue),
- style = ElementTheme.typography.aliasButtonText
- )
- }
+ )
}
}
}
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt
index db0ea8c11d..001c048e4e 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt
@@ -43,10 +43,11 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.text.applyScaleDown
+import io.element.android.libraries.designsystem.text.toSp
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
-import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
@@ -114,7 +115,11 @@ private fun DefaultRoomListTopBar(
val fontStyle = if (scrollBehavior.state.collapsedFraction > 0.5)
ElementTheme.typography.aliasScreenTitle
else
- ElementTheme.typography.fontHeadingLgBold
+ ElementTheme.typography.fontHeadingLgBold.copy(
+ // Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated,
+ // ensure that the font size will never be bigger than 28.dp.
+ fontSize = 28.dp.applyScaleDown().toSp()
+ )
Text(
style = fontStyle,
text = stringResource(id = R.string.screen_roomlist_main_space_title)
@@ -163,7 +168,7 @@ private fun DefaultRoomListTopBar(
showMenu = false
onMenuActionClicked(RoomListMenuAction.InviteFriends)
},
- text = { DropdownMenuItemText(stringResource(id = CommonStrings.action_invite)) },
+ text = { Text(stringResource(id = CommonStrings.action_invite)) },
leadingIcon = {
Icon(
Icons.Outlined.Share,
@@ -177,7 +182,7 @@ private fun DefaultRoomListTopBar(
showMenu = false
onMenuActionClicked(RoomListMenuAction.ReportBug)
},
- text = { DropdownMenuItemText(stringResource(id = CommonStrings.common_report_a_bug)) },
+ text = { Text(stringResource(id = CommonStrings.common_report_a_bug)) },
leadingIcon = {
Icon(
Icons.Outlined.BugReport,
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
index b32f169e6b..aab174c6a4 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
@@ -155,7 +155,7 @@ private fun RowScope.NameAndTimestampRow(room: RoomListRoomSummary) {
@Composable
private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) {
// Last Message
- val attributedLastMessage = (room.lastMessage as? AnnotatedString)
+ val attributedLastMessage = room.lastMessage as? AnnotatedString
?: AnnotatedString(room.lastMessage.orEmpty().toString())
Text(
modifier = Modifier
@@ -186,10 +186,10 @@ class PercentRectangleSizeShape(private val percent: Float) : Shape {
val halfPercent = percent / 2f
val path = Path().apply {
val rect = Rect(
- 0f,
- size.height * halfPercent,
- size.width,
- size.height - (size.height * halfPercent)
+ left = 0f,
+ top = size.height * halfPercent,
+ right = size.width,
+ bottom = size.height * (1 - halfPercent)
)
addRect(rect)
close()
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt
index 3a89014799..e05efd3ddd 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt
@@ -30,7 +30,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummary
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.coroutines.withContext
import javax.inject.Inject
@@ -44,8 +44,9 @@ class DefaultInviteStateDataSource @Inject constructor(
@Composable
override fun inviteState(): InvitesState {
val invites by client
- .roomSummaryDataSource
- .inviteRooms()
+ .roomListService
+ .invites()
+ .summaries
.collectAsState()
val seenInvites by seenInvitesStore
diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt
index 8602f15910..e44bcd6b6b 100644
--- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt
+++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt
@@ -18,6 +18,8 @@ package io.element.android.features.roomlist.impl.datasource
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders
+import io.element.android.libraries.androidutils.diff.DiffCacheUpdater
+import io.element.android.libraries.androidutils.diff.MutableListDiffCache
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.orEmpty
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
@@ -25,8 +27,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummary
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -36,11 +38,13 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import javax.inject.Inject
class RoomListDataSource @Inject constructor(
- private val roomSummaryDataSource: RoomSummaryDataSource,
+ private val roomListService: RoomListService,
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
private val roomLastMessageFormatter: RoomLastMessageFormatter,
private val coroutineDispatchers: CoroutineDispatchers,
@@ -50,15 +54,18 @@ class RoomListDataSource @Inject constructor(
private val _allRooms = MutableStateFlow>(persistentListOf())
private val _filteredRooms = MutableStateFlow>(persistentListOf())
+ private val lock = Mutex()
+ private val diffCache = MutableListDiffCache()
+ private val diffCacheUpdater = DiffCacheUpdater(diffCache = diffCache, detectMoves = true) { old, new ->
+ old?.identifier() == new?.identifier()
+ }
+
fun launchIn(coroutineScope: CoroutineScope) {
- roomSummaryDataSource
+ roomListService
.allRooms()
+ .summaries
.onEach { roomSummaries ->
- _allRooms.value = if (roomSummaries.isEmpty()) {
- RoomListRoomSummaryPlaceholders.createFakeList(16)
- } else {
- mapRoomSummaries(roomSummaries)
- }.toImmutableList()
+ replaceWith(roomSummaries)
}
.launchIn(coroutineScope)
@@ -73,7 +80,8 @@ class RoomListDataSource @Inject constructor(
}
.onEach {
_filteredRooms.value = it
- }.launchIn(coroutineScope)
+ }
+ .launchIn(coroutineScope)
}
fun updateFilter(filterValue: String) {
@@ -84,33 +92,63 @@ class RoomListDataSource @Inject constructor(
val allRooms: StateFlow> = _allRooms
val filteredRooms: StateFlow> = _filteredRooms
- private suspend fun mapRoomSummaries(
- roomSummaries: List
- ): List = withContext(coroutineDispatchers.computation) {
- roomSummaries.map { roomSummary ->
- when (roomSummary) {
- is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
- is RoomSummary.Filled -> {
- val avatarData = AvatarData(
- id = roomSummary.identifier(),
- name = roomSummary.details.name,
- url = roomSummary.details.avatarURLString,
- size = AvatarSize.RoomListItem,
- )
- val roomIdentifier = roomSummary.identifier()
- RoomListRoomSummary(
- id = roomSummary.identifier(),
- roomId = RoomId(roomIdentifier),
- name = roomSummary.details.name,
- hasUnread = roomSummary.details.unreadNotificationCount > 0,
- timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
- lastMessage = roomSummary.details.lastMessage?.let { message ->
- roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
- }.orEmpty(),
- avatarData = avatarData,
- )
- }
- }
+ private suspend fun replaceWith(roomSummaries: List) = withContext(coroutineDispatchers.computation) {
+ lock.withLock {
+ diffCacheUpdater.updateWith(roomSummaries)
+ buildAndEmitAllRooms(roomSummaries)
}
}
+
+ private suspend fun buildAndEmitAllRooms(roomSummaries: List) {
+ if (diffCache.isEmpty()) {
+ _allRooms.emit(
+ RoomListRoomSummaryPlaceholders.createFakeList(16).toImmutableList()
+ )
+ } else {
+ val roomListRoomSummaries = ArrayList()
+ for (index in diffCache.indices()) {
+ val cacheItem = diffCache.get(index)
+ if (cacheItem == null) {
+ buildAndCacheItem(roomSummaries, index)?.also { timelineItemState ->
+ roomListRoomSummaries.add(timelineItemState)
+ }
+ } else {
+ roomListRoomSummaries.add(cacheItem)
+ }
+ }
+ _allRooms.emit(roomListRoomSummaries.toImmutableList())
+ }
+ }
+
+ private fun buildAndCacheItem(
+ roomSummaries: List,
+ index: Int
+ ): RoomListRoomSummary? {
+ val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) {
+ is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
+ is RoomSummary.Filled -> {
+ val avatarData = AvatarData(
+ id = roomSummary.identifier(),
+ name = roomSummary.details.name,
+ url = roomSummary.details.avatarURLString,
+ size = AvatarSize.RoomListItem,
+ )
+ val roomIdentifier = roomSummary.identifier()
+ RoomListRoomSummary(
+ id = roomSummary.identifier(),
+ roomId = RoomId(roomIdentifier),
+ name = roomSummary.details.name,
+ hasUnread = roomSummary.details.unreadNotificationCount > 0,
+ timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
+ lastMessage = roomSummary.details.lastMessage?.let { message ->
+ roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
+ }.orEmpty(),
+ avatarData = avatarData,
+ )
+ }
+ null -> null
+ }
+ diffCache[index] = roomListRoomSummary
+ return roomListRoomSummary
+ }
}
diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..fcd91c56e3
--- /dev/null
+++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "Создайте новую беседу или комнату"
+ "Начните переписку с отправки сообщения."
+ "Пока нет доступных чатов."
+ "Все чаты"
+ "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям в дальнейшем, проверьте их на другом устройстве."
+ "Подтвердите, что это вы"
+
diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml
index 250822f4c9..0a1879a484 100644
--- a/features/roomlist/impl/src/main/res/values-sk/translations.xml
+++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml
@@ -1,6 +1,8 @@
"Vytvorte novú konverzáciu alebo miestnosť"
+ "Začnite tým, že niekomu pošlete správu."
+ "Zatiaľ žiadne konverzácie."
"Všetky konverzácie"
"Vyzerá to tak, že používate nové zariadenie. Overte svoj prístup k zašifrovaným správam pomocou vášho druhého zariadenia."
"Overte, že ste to vy"
diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..b4d22c5b26
--- /dev/null
+++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,4 @@
+
+
+ "建立新的對話或聊天室"
+
diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml
index 3a1c3cbad6..d63f96d07c 100644
--- a/features/roomlist/impl/src/main/res/values/localazy.xml
+++ b/features/roomlist/impl/src/main/res/values/localazy.xml
@@ -1,6 +1,8 @@
"Create a new conversation or room"
+ "Get started by messaging someone."
+ "No chats yet."
"All Chats"
"Looks like you’re using a new device. Verify with another device to access your encrypted messages moving forwards."
"Verify it’s you"
diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
index 0ead16da45..eaa3801e12 100644
--- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
+++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.roomlist.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -47,9 +47,10 @@ import io.element.android.libraries.matrix.test.A_ROOM_NAME
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
-import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
+import io.element.android.tests.testutils.consumeItemsUntilPredicate
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
@@ -61,7 +62,7 @@ class RoomListPresenterTests {
@Test
fun `present - should start with no user and then load user with success`() = runTest {
val presenter = createRoomListPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -81,7 +82,7 @@ class RoomListPresenterTests {
userAvatarURLString = Result.failure(AN_EXCEPTION),
)
val presenter = createRoomListPresenter(matrixClient)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -94,7 +95,7 @@ class RoomListPresenterTests {
@Test
fun `present - should filter room with success`() = runTest {
val presenter = createRoomListPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -110,21 +111,20 @@ class RoomListPresenterTests {
@Test
fun `present - load 1 room with success`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val matrixClient = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource
+ roomListService = roomListService
)
val presenter = createRoomListPresenter(matrixClient)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- skipItems(1)
- val initialState = awaitItem()
+ val initialState = consumeItemsUntilPredicate { state -> state.roomList.size == 16 }.last()
// Room list is loaded with 16 placeholders
Truth.assertThat(initialState.roomList.size).isEqualTo(16)
Truth.assertThat(initialState.roomList.all { it.isPlaceholder }).isTrue()
- roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled()))
- val withRoomState = awaitItem()
+ roomListService.postAllRooms(listOf(aRoomSummaryFilled()))
+ val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last()
Truth.assertThat(withRoomState.roomList.size).isEqualTo(1)
Truth.assertThat(withRoomState.roomList.first())
.isEqualTo(aRoomListRoomSummary)
@@ -133,68 +133,66 @@ class RoomListPresenterTests {
@Test
fun `present - load 1 room with success and filter rooms`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val matrixClient = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource
+ roomListService = roomListService
)
val presenter = createRoomListPresenter(matrixClient)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled()))
- skipItems(1)
- val loadedState = awaitItem()
+ roomListService.postAllRooms(listOf(aRoomSummaryFilled()))
+ val loadedState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last()
// Test filtering with result
loadedState.eventSink.invoke(RoomListEvents.UpdateFilter(A_ROOM_NAME.substring(0, 3)))
- skipItems(1) // Filter update
- val withNotFilteredRoomState = awaitItem()
- Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3))
- Truth.assertThat(withNotFilteredRoomState.filteredRoomList.size).isEqualTo(1)
- Truth.assertThat(withNotFilteredRoomState.filteredRoomList.first())
+ val withFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 1 }.last()
+ Truth.assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3))
+ Truth.assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1)
+ Truth.assertThat(withFilteredRoomState.filteredRoomList.first())
.isEqualTo(aRoomListRoomSummary)
// Test filtering without result
- withNotFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada"))
- skipItems(1) // Filter update
- Truth.assertThat(awaitItem().filter).isEqualTo("tada")
- Truth.assertThat(awaitItem().filteredRoomList).isEmpty()
+ withFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada"))
+ val withNotFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 0 }.last()
+ Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo("tada")
+ Truth.assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty()
}
}
@Test
fun `present - update visible range`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val matrixClient = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource
+ roomListService = roomListService
)
val presenter = createRoomListPresenter(matrixClient)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled()))
+ roomListService.postAllRooms(listOf(aRoomSummaryFilled()))
val loadedState = awaitItem()
// check initial value
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange).isNull()
+ Truth.assertThat(roomListService.latestSlidingSyncRange).isNull()
// Test empty range
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(1, 0)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange).isNull()
+ Truth.assertThat(roomListService.latestSlidingSyncRange).isNull()
// Update visible range and check that range is transmitted to the SDK after computation
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 0)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange)
+ Truth.assertThat(roomListService.latestSlidingSyncRange)
.isEqualTo(IntRange(0, 20))
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 1)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange)
+ Truth.assertThat(roomListService.latestSlidingSyncRange)
.isEqualTo(IntRange(0, 21))
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(19, 29)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange)
+ Truth.assertThat(roomListService.latestSlidingSyncRange)
.isEqualTo(IntRange(0, 49))
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(49, 59)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange)
+ Truth.assertThat(roomListService.latestSlidingSyncRange)
.isEqualTo(IntRange(29, 79))
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 159)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange)
+ Truth.assertThat(roomListService.latestSlidingSyncRange)
.isEqualTo(IntRange(129, 179))
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 259)))
- Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange)
+ Truth.assertThat(roomListService.latestSlidingSyncRange)
.isEqualTo(IntRange(129, 279))
cancelAndIgnoreRemainingEvents()
}
@@ -202,9 +200,9 @@ class RoomListPresenterTests {
@Test
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
+ val roomListService = FakeRoomListService()
val matrixClient = FakeMatrixClient(
- roomSummaryDataSource = roomSummaryDataSource
+ roomListService = roomListService
)
val presenter = createRoomListPresenter(
client = matrixClient,
@@ -213,7 +211,7 @@ class RoomListPresenterTests {
givenVerifiedStatus(SessionVerifiedStatus.NotVerified)
},
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val eventSink = awaitItem().eventSink
@@ -229,7 +227,7 @@ class RoomListPresenterTests {
val inviteStateFlow = MutableStateFlow(InvitesState.NoInvites)
val inviteStateDataSource = FakeInviteDataSource(inviteStateFlow)
val presenter = createRoomListPresenter(inviteStateDataSource = inviteStateDataSource)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -249,7 +247,7 @@ class RoomListPresenterTests {
@Test
fun `present - show context menu`() = runTest {
val presenter = createRoomListPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -267,7 +265,7 @@ class RoomListPresenterTests {
@Test
fun `present - hide context menu`() = runTest {
val presenter = createRoomListPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -290,7 +288,7 @@ class RoomListPresenterTests {
fun `present - leave room calls into leave room presenter`() = runTest {
val leaveRoomPresenter = LeaveRoomPresenterFake()
val presenter = createRoomListPresenter(leaveRoomPresenter = leaveRoomPresenter)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -319,7 +317,7 @@ class RoomListPresenterTests {
inviteStateDataSource = inviteStateDataSource,
leaveRoomPresenter = leaveRoomPresenter,
roomListDataSource = RoomListDataSource(
- client.roomSummaryDataSource,
+ client.roomListService,
lastMessageTimestampFormatter,
roomLastMessageFormatter,
coroutineDispatchers = testCoroutineDispatchers()
diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt
index b67a6c6b43..ce7bf685a8 100644
--- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt
+++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt
@@ -16,7 +16,7 @@
package io.element.android.features.roomlist.impl.datasource
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
@@ -25,8 +25,8 @@ import io.element.android.features.roomlist.impl.InvitesState
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
import io.element.android.libraries.matrix.test.FakeMatrixClient
-import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -35,12 +35,12 @@ internal class DefaultInviteStateDataSourceTest {
@Test
fun `emits NoInvites state if invites list is empty`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ val client = FakeMatrixClient(roomListService = roomListService)
val seenStore = FakeSeenInvitesStore()
val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers())
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
dataSource.inviteState()
}.test {
Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites)
@@ -49,13 +49,13 @@ internal class DefaultInviteStateDataSourceTest {
@Test
fun `emits NewInvites state if unseen invite exists`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID)))
- val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID)))
+ val client = FakeMatrixClient(roomListService = roomListService)
val seenStore = FakeSeenInvitesStore()
val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers())
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
dataSource.inviteState()
}.test {
skipItems(1)
@@ -65,14 +65,14 @@ internal class DefaultInviteStateDataSourceTest {
@Test
fun `emits NewInvites state if multiple invites exist and at least one is unseen`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2)))
- val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2)))
+ val client = FakeMatrixClient(roomListService = roomListService)
val seenStore = FakeSeenInvitesStore()
seenStore.publishRoomIds(setOf(A_ROOM_ID))
val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers(useUnconfinedTestDispatcher = true))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
dataSource.inviteState()
}.test {
skipItems(1)
@@ -82,14 +82,14 @@ internal class DefaultInviteStateDataSourceTest {
@Test
fun `emits SeenInvites state if invite exists in seen store`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID)))
- val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID)))
+ val client = FakeMatrixClient(roomListService = roomListService)
val seenStore = FakeSeenInvitesStore()
seenStore.publishRoomIds(setOf(A_ROOM_ID))
val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers(useUnconfinedTestDispatcher = true))
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
dataSource.inviteState()
}.test {
skipItems(1)
@@ -100,19 +100,19 @@ internal class DefaultInviteStateDataSourceTest {
@Test
fun `emits new state in response to upstream events`() = runTest {
- val roomSummaryDataSource = FakeRoomSummaryDataSource()
- val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource)
+ val roomListService = FakeRoomListService()
+ val client = FakeMatrixClient(roomListService = roomListService)
val seenStore = FakeSeenInvitesStore()
val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers())
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
dataSource.inviteState()
}.test {
// Initially there are no invites
Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites)
// When a single invite is received, state should be NewInvites
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID)))
+ roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID)))
skipItems(1)
Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites)
@@ -122,12 +122,12 @@ internal class DefaultInviteStateDataSourceTest {
Truth.assertThat(awaitItem()).isEqualTo(InvitesState.SeenInvites)
// Another new invite resets it to NewInvites
- roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2)))
+ roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2)))
skipItems(1)
Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites)
// All of the invites going away reverts to NoInvites
- roomSummaryDataSource.postInviteRooms(emptyList())
+ roomListService.postInviteRooms(emptyList())
skipItems(1)
Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites)
}
diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
index 77984a3bb2..0ffd518669 100644
--- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
+++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -44,15 +43,16 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
-import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.aliasButtonText
+import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
+import kotlinx.coroutines.sync.Mutex
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep
@Composable
@@ -75,6 +75,7 @@ fun VerifySelfSessionView(
val buttonsVisible by remember(verificationFlowStep) {
derivedStateOf { verificationFlowStep != FlowStep.AwaitingOtherDeviceResponse && verificationFlowStep != FlowStep.Completed }
}
+ Mutex()
HeaderFooterPage(
modifier = modifier,
header = {
@@ -219,35 +220,33 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
ButtonColumnMolecule(
modifier = Modifier.padding(bottom = 20.dp)
) {
- ButtonWithProgress(
- text = positiveButtonTitle?.let { stringResource(it) },
- showProgress = isVerifying,
- modifier = Modifier.fillMaxWidth(),
- onClick = { positiveButtonEvent?.let { eventSink(it) } }
- )
+ if (positiveButtonTitle != null) {
+ Button(
+ text = stringResource(positiveButtonTitle),
+ showProgress = isVerifying,
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { positiveButtonEvent?.let { eventSink(it) } }
+ )
+ }
if (negativeButtonTitle != null) {
TextButton(
+ text = stringResource(negativeButtonTitle),
modifier = Modifier.fillMaxWidth(),
onClick = negativeButtonCallback,
enabled = negativeButtonEnabled,
- ) {
- Text(
- text = stringResource(negativeButtonTitle),
- style = ElementTheme.typography.aliasButtonText,
- )
- }
+ )
}
}
}
@Preview
@Composable
-fun VerifySelfSessionViewLightPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) =
+internal fun VerifySelfSessionViewLightPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun VerifySelfSessionViewDarkPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) =
+internal fun VerifySelfSessionViewDarkPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..552204fd7a
--- /dev/null
+++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,19 @@
+
+
+ "Кажется, что-то не так. Время ожидания запроса истекло, либо запрос был отклонен."
+ "Убедитесь, что приведенные ниже смайлики совпадают со смайликами, показанными во время другого сеанса."
+ "Сравните смайлики"
+ "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное."
+ "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы."
+ "Открыть существующий сеанс"
+ "Повторить проверку"
+ "Я готов"
+ "Ожидание соответствия"
+ "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке."
+ "Они не совпадают"
+ "Они совпадают"
+ "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе."
+ "Ожидание принятия запроса"
+ "Проверка отменена"
+ "Начать"
+
diff --git a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..fc59911a93
--- /dev/null
+++ b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,9 @@
+
+
+ "我準備好了"
+ "等待比對"
+ "不相符"
+ "相符"
+ "驗證已取消"
+ "開始"
+
diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
index 0b58c125de..82664f0e03 100644
--- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
+++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
@@ -16,7 +16,7 @@
package io.element.android.features.verifysession.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test
@@ -36,7 +36,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Initial state is received`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial)
@@ -47,7 +47,7 @@ class VerifySelfSessionPresenterTests {
fun `present - Handles requestVerification`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
requestVerificationAndAwaitVerifyingState(service)
@@ -58,7 +58,7 @@ class VerifySelfSessionPresenterTests {
fun `present - Handles startSasVerification`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -77,7 +77,7 @@ class VerifySelfSessionPresenterTests {
@Test
fun `present - Cancelation on initial state does nothing`() = runTest {
val presenter = createPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -92,7 +92,7 @@ class VerifySelfSessionPresenterTests {
fun `present - A fail in the flow cancels it`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = requestVerificationAndAwaitVerifyingState(service)
@@ -109,7 +109,7 @@ class VerifySelfSessionPresenterTests {
fun `present - Canceling the flow once it's verifying cancels it`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = requestVerificationAndAwaitVerifyingState(service)
@@ -123,7 +123,7 @@ class VerifySelfSessionPresenterTests {
fun `present - When verifying, if we receive another challenge we ignore it`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
requestVerificationAndAwaitVerifyingState(service)
@@ -136,7 +136,7 @@ class VerifySelfSessionPresenterTests {
fun `present - Restart after cancelation returns to requesting verification`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = requestVerificationAndAwaitVerifyingState(service)
@@ -158,7 +158,7 @@ class VerifySelfSessionPresenterTests {
givenEmojiList(emojis)
}
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = requestVerificationAndAwaitVerifyingState(service)
@@ -172,7 +172,7 @@ class VerifySelfSessionPresenterTests {
fun `present - When verification is declined, the flow is canceled`() = runTest {
val service = FakeSessionVerificationService()
val presenter = createPresenter(service)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val state = requestVerificationAndAwaitVerifyingState(service)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index f0759ee705..ca23a17afd 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -6,7 +6,7 @@
android_gradle_plugin = "8.0.2"
kotlin = "1.8.22"
ksp = "1.8.22-1.0.11"
-molecule = "0.11.0"
+molecule = "1.2.0"
# AndroidX
material = "1.9.0"
@@ -14,11 +14,11 @@ core = "1.10.1"
datastore = "1.0.0"
constraintlayout = "2.1.4"
constraintlayout_compose = "1.0.1"
-recyclerview = "1.3.0"
+recyclerview = "1.3.1"
lifecycle = "2.6.1"
activity = "1.7.2"
startup = "1.1.1"
-media3 = "1.1.0"
+media3 = "1.1.1"
browser = "1.5.0"
# Compose
@@ -45,11 +45,11 @@ dependencycheck = "8.3.1"
dependencyanalysis = "1.20.0"
stem = "2.3.0"
sqldelight = "1.5.5"
-telephoto = "0.4.0"
+telephoto = "0.5.0"
# DI
dagger = "2.47"
-anvil = "2.4.6"
+anvil = "2.4.7-1-8"
# Auto service
autoservice = "1.1.1"
@@ -65,7 +65,7 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref
android_desugar = "com.android.tools:desugar_jdk_libs:2.0.3"
kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
# https://firebase.google.com/docs/android/setup#available-libraries
-google_firebase_bom = "com.google.firebase:firebase-bom:32.2.0"
+google_firebase_bom = "com.google.firebase:firebase-bom:32.2.2"
# AndroidX
androidx_material = { module = "com.google.android.material:material", version.ref = "material" }
@@ -123,7 +123,7 @@ test_junit = "junit:junit:4.13.2"
test_runner = "androidx.test:runner:1.5.2"
test_uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0"
test_junitext = "androidx.test.ext:junit:1.1.5"
-test_mockk = "io.mockk:mockk:1.13.5"
+test_mockk = "io.mockk:mockk:1.13.7"
test_barista = "com.adevinta.android:barista:4.3.0"
test_hamcrest = "org.hamcrest:hamcrest:2.2"
test_orchestrator = "androidx.test:orchestrator:1.4.2"
@@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
timber = "com.jakewharton.timber:timber:5.0.1"
-matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.34"
+matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.42"
sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
@@ -156,14 +156,14 @@ otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5"
vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0"
vanniktech_emoji = "com.vanniktech:emoji-google:0.16.0"
telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" }
-statemachine = "com.freeletics.flowredux:compose:1.1.0"
+statemachine = "com.freeletics.flowredux:compose:1.2.0"
maplibre = "org.maplibre.gl:android-sdk:10.2.0"
maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.0"
maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.0"
# Analytics
posthog = "com.posthog.android:posthog:2.0.3"
-sentry_android = "io.sentry:sentry-android:6.26.0"
+sentry = "io.sentry:sentry-android:6.28.0"
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:42b2faa417c1e95f430bf8f6e379adba25ad5ef8"
# Di
@@ -196,7 +196,7 @@ kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
anvil = { id = "com.squareup.anvil", version.ref = "anvil" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
-ktlint = "org.jlleitschuh.gradle.ktlint:11.5.0"
+ktlint = "org.jlleitschuh.gradle.ktlint:11.5.1"
dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" }
dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" }
dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyanalysis" }
diff --git a/libraries/androidutils/build.gradle.kts b/libraries/androidutils/build.gradle.kts
index 92e3c46126..57e4ca3569 100644
--- a/libraries/androidutils/build.gradle.kts
+++ b/libraries/androidutils/build.gradle.kts
@@ -37,6 +37,7 @@ dependencies {
implementation(libs.timber)
implementation(libs.androidx.corektx)
implementation(libs.androidx.activity.activity)
+ implementation(libs.androidx.recyclerview)
implementation(libs.androidx.exifinterface)
implementation(libs.androidx.security.crypto)
implementation(libs.androidx.browser)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt
similarity index 70%
rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt
rename to libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt
index 4a78447bd7..219441d5e6 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright (c) 2023 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.
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-package io.element.android.features.messages.impl.timeline.diff
+package io.element.android.libraries.androidutils.diff
import androidx.recyclerview.widget.DiffUtil
-import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
-internal class MatrixTimelineItemsDiffCallback(
- private val oldList: List,
- private val newList: List
+/**
+ * Default implementation of [DiffUtil.Callback] that uses [areItemsTheSame] to compare items.
+ */
+internal class DefaultDiffCallback(
+ private val oldList: List,
+ private val newList: List,
+ private val areItemsTheSame: (oldItem: T?, newItem: T?) -> Boolean,
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
@@ -35,11 +38,7 @@ internal class MatrixTimelineItemsDiffCallback(
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList.getOrNull(oldItemPosition)
val newItem = newList.getOrNull(newItemPosition)
- return if (oldItem is MatrixTimelineItem.Event && newItem is MatrixTimelineItem.Event) {
- oldItem.uniqueId == newItem.uniqueId
- } else {
- false
- }
+ return areItemsTheSame(oldItem, newItem)
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt
new file mode 100644
index 0000000000..3d1161e2e0
--- /dev/null
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.androidutils.diff
+
+/**
+ * A cache that can be used to store some data that can be invalidated when a diff is applied.
+ * The cache is invalidated by the [DiffCacheInvalidator].
+ */
+interface DiffCache {
+ fun get(index: Int): E?
+ fun indices(): IntRange
+ fun isEmpty(): Boolean
+}
+
+/**
+ * A [DiffCache] that can be mutated by adding, removing or updating elements.
+ */
+interface MutableDiffCache : DiffCache {
+ fun removeAt(index: Int): E?
+ fun add(index: Int, element: E?)
+ operator fun set(index: Int, element: E?)
+}
+
+/**
+ * A [MutableDiffCache] backed by a [MutableList].
+ *
+ */
+class MutableListDiffCache(private val mutableList: MutableList = ArrayList()) : MutableDiffCache {
+
+ override fun removeAt(index: Int): E? {
+ return mutableList.removeAt(index)
+ }
+
+ override fun get(index: Int): E? {
+ return mutableList.getOrNull(index)
+ }
+
+ override fun indices(): IntRange {
+ return mutableList.indices
+ }
+
+ override fun isEmpty(): Boolean {
+ return mutableList.isEmpty()
+ }
+
+ override operator fun set(index: Int, element: E?) {
+ mutableList[index] = element
+ }
+
+ override fun add(index: Int, element: E?) {
+ mutableList.add(index, element)
+ }
+}
diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt
new file mode 100644
index 0000000000..4ebdc3224f
--- /dev/null
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.androidutils.diff
+
+/**
+ * [DiffCacheInvalidator] is used to invalidate the cache when the list is updated.
+ * It is used by [DiffCacheUpdater].
+ * Check the default implementation [DefaultDiffCacheInvalidator].
+ */
+interface DiffCacheInvalidator {
+ fun onChanged(position: Int, count: Int, cache: MutableDiffCache)
+
+ fun onMoved(fromPosition: Int, toPosition: Int, cache: MutableDiffCache)
+
+ fun onInserted(position: Int, count: Int, cache: MutableDiffCache)
+
+ fun onRemoved(position: Int, count: Int, cache: MutableDiffCache)
+}
+
+/**
+ * Default implementation of [DiffCacheInvalidator].
+ * It invalidates the cache by setting values to null.
+ */
+class DefaultDiffCacheInvalidator : DiffCacheInvalidator {
+
+ override fun onChanged(position: Int, count: Int, cache: MutableDiffCache) {
+ for (i in position until position + count) {
+ // Invalidate cache
+ cache[i] = null
+ }
+ }
+
+ override fun onMoved(fromPosition: Int, toPosition: Int, cache: MutableDiffCache) {
+ val model = cache.removeAt(fromPosition)
+ cache.add(toPosition, model)
+ }
+
+ override fun onInserted(position: Int, count: Int, cache: MutableDiffCache) {
+ repeat(count) {
+ cache.add(position, null)
+ }
+ }
+
+ override fun onRemoved(position: Int, count: Int, cache: MutableDiffCache) {
+ repeat(count) {
+ cache.removeAt(position)
+ }
+ }
+}
diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt
new file mode 100644
index 0000000000..500edcb135
--- /dev/null
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.androidutils.diff
+
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListUpdateCallback
+import timber.log.Timber
+import kotlin.system.measureTimeMillis
+
+/**
+ * Class in charge of updating a [MutableDiffCache] according to the cache invalidation rules provided by the [DiffCacheInvalidator].
+ * @param ListItem the type of the items in the list
+ * @param CachedItem the type of the items in the cache
+ * @param diffCache the cache to update
+ * @param detectMoves true if DiffUtil should try to detect moved items, false otherwise
+ * @param cacheInvalidator the invalidator to use to update the cache
+ * @param areItemsTheSame the function to use to compare items
+ */
+class DiffCacheUpdater(
+ private val diffCache: MutableDiffCache,
+ private val detectMoves: Boolean = false,
+ private val cacheInvalidator: DiffCacheInvalidator = DefaultDiffCacheInvalidator(),
+ private val areItemsTheSame: (oldItem: ListItem?, newItem: ListItem?) -> Boolean,
+) {
+
+ private val lock = Object()
+ private var prevOriginalList: List = emptyList()
+
+ private val listUpdateCallback = object : ListUpdateCallback {
+ override fun onInserted(position: Int, count: Int) {
+ cacheInvalidator.onInserted(position, count, diffCache)
+ }
+
+ override fun onRemoved(position: Int, count: Int) {
+ cacheInvalidator.onRemoved(position, count, diffCache)
+ }
+
+ override fun onMoved(fromPosition: Int, toPosition: Int) {
+ cacheInvalidator.onMoved(fromPosition, toPosition, diffCache)
+ }
+
+ override fun onChanged(position: Int, count: Int, payload: Any?) {
+ cacheInvalidator.onChanged(position, count, diffCache)
+ }
+ }
+
+ fun updateWith(newOriginalList: List) = synchronized(lock) {
+ val timeToDiff = measureTimeMillis {
+ val diffCallback = DefaultDiffCallback(prevOriginalList, newOriginalList, areItemsTheSame)
+ val diffResult = DiffUtil.calculateDiff(diffCallback, detectMoves)
+ prevOriginalList = newOriginalList
+ diffResult.dispatchUpdatesTo(listUpdateCallback)
+ }
+ Timber.v("Time to apply diff on new list of ${newOriginalList.size} items: $timeToDiff ms")
+ }
+}
diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
index 65a5dc9e0d..a9a17dcceb 100644
--- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
@@ -59,7 +59,7 @@ fun Context.isAnimationEnabled(): Boolean {
}
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
-fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
/**
* Return the application label of the provided package. If not found, the package is returned.
@@ -183,7 +183,7 @@ fun Context.startInstallFromSourceIntent(
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
- .setData(Uri.parse(String.format("package:%s", packageName)))
+ .setData(Uri.parse("package:$packageName"))
try {
activityResultLauncher.launch(intent)
} catch (activityNotFoundException: ActivityNotFoundException) {
diff --git a/libraries/androidutils/src/main/res/values-ru/translations.xml b/libraries/androidutils/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..bb236dc893
--- /dev/null
+++ b/libraries/androidutils/src/main/res/values-ru/translations.xml
@@ -0,0 +1,4 @@
+
+
+ "Не найдено совместимое приложение для обработки этого действия."
+
diff --git a/libraries/androidutils/src/main/res/values/integers.xml b/libraries/androidutils/src/main/res/values/integers.xml
index ecbfa4cdda..2f9e641bdf 100644
--- a/libraries/androidutils/src/main/res/values/integers.xml
+++ b/libraries/androidutils/src/main/res/values/integers.xml
@@ -15,9 +15,9 @@
~ limitations under the License.
-->
-
+
- 1
- 0
+ 1
+ 0
diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt
index b96d9e166b..534c9d741b 100644
--- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt
+++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt
@@ -23,5 +23,5 @@ import com.bumble.appyx.core.plugin.plugins
interface NodeInputs : Plugin
inline fun Node.inputs(): I {
- return plugins().firstOrNull() ?: throw RuntimeException("Make sure to actually pass NodeInputs plugin to your node")
+ return requireNotNull(plugins().firstOrNull()) { "Make sure to actually pass NodeInputs plugin to your node" }
}
diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt
index b91d249547..fe801e71f7 100644
--- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt
+++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt
@@ -16,11 +16,11 @@
package io.element.android.libraries.core.data
-inline fun tryOrNull(noinline onError: ((Throwable) -> Unit)? = null, operation: () -> A): A? {
+inline fun tryOrNull(onError: ((Throwable) -> Unit) = { }, operation: () -> A): A? {
return try {
operation()
} catch (any: Throwable) {
- onError?.invoke(any)
+ onError.invoke(any)
null
}
}
diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt
index db07432df0..343f5ce351 100644
--- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt
+++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt
@@ -72,7 +72,3 @@ fun String.ellipsize(length: Int): String {
return "${this.take(length)}…"
}
-
-inline fun Any?.takeAs(): R? {
- return takeIf { it is R } as R?
-}
diff --git a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt
index 483e27af71..5aefcdcd7b 100644
--- a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt
+++ b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt
@@ -101,7 +101,7 @@ class DefaultLastMessageTimestampFormatterTest {
* Create DefaultLastMessageFormatter and set current time to the provided date.
*/
private fun createFormatter(@Suppress("SameParameterValue") currentDate: String): LastMessageTimestampFormatter {
- val clock = FakeClock().also { it.givenInstant(Instant.parse(currentDate)) }
+ val clock = FakeClock().apply { givenInstant(Instant.parse(currentDate)) }
val localDateTimeProvider = LocalDateTimeProvider(clock, TimeZone.UTC)
val dateFormatters = DateFormatters(Locale.US, clock, TimeZone.UTC)
return DefaultLastMessageTimestampFormatter(localDateTimeProvider, dateFormatters)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt
index 0e33567129..47b951baa2 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt
@@ -25,4 +25,6 @@ object VectorIcons {
val DoorOpen = R.drawable.ic_door_open_24
val DeveloperMode = R.drawable.ic_developer_mode
val ReportContent = R.drawable.ic_report_content
+ val Groups = R.drawable.ic_groups
+ val Share = R.drawable.ic_share
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt
index 94e50d5953..8f96ed6b6e 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt
@@ -138,7 +138,7 @@ fun Modifier.shapeShadow(
val paint = Paint()
val frameworkPaint = paint.asFrameworkPaint()
if (blurRadius != 0.dp) {
- frameworkPaint.maskFilter = (BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL))
+ frameworkPaint.maskFilter = BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL)
}
frameworkPaint.color = color.toArgb()
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt
index 6b20c96880..2af9e77b99 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt
@@ -70,7 +70,7 @@ fun InfoListItemMolecule(
@DayNightPreviews
@Composable
-fun InfoListItemMoleculePreview() {
+internal fun InfoListItemMoleculePreview() {
ElementPreview {
val color = if (isSystemInDarkTheme()) Color.DarkGray else Color.LightGray
Column(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt
index a8ad97a950..9fc688f227 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt
@@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
-import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.TextButton
@Composable
@@ -59,11 +59,8 @@ internal fun ButtonColumnMoleculeDarkPreview() =
@Composable
private fun ContentToPreview() {
ButtonColumnMolecule {
- Button(onClick = {}, modifier = Modifier.fillMaxWidth()) {
- Text(text = "Button")
- }
- TextButton(onClick = {}, modifier = Modifier.fillMaxWidth()) {
- Text(text = "TextButton")
- }
+ Button(text = "Button", onClick = {}, modifier = Modifier.fillMaxWidth())
+ OutlinedButton(text = "OutlinedButton", onClick = {}, modifier = Modifier.fillMaxWidth())
+ TextButton(text = "TextButton", onClick = {}, modifier = Modifier.fillMaxWidth())
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt
index b8b5a2146f..7c03a2106e 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt
@@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
-import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
@Composable
@@ -54,11 +53,7 @@ internal fun ButtonRowMoleculeDarkPreview() =
@Composable
private fun ContentToPreview() {
ButtonRowMolecule {
- TextButton(onClick = { }) {
- Text("Button 1")
- }
- TextButton(onClick = { }) {
- Text("Button 2")
- }
+ TextButton(text = "Button 1", onClick = {})
+ TextButton(text = "Button 2", onClick = {})
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt
new file mode 100644
index 0000000000..1a10aa2886
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.atomic.molecules
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
+import io.element.android.libraries.designsystem.components.avatar.AvatarSize
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.theme.placeholderBackground
+import io.element.android.libraries.theme.ElementTheme
+
+@Composable
+fun IconTitlePlaceholdersRowMolecule(
+ iconSize: Dp,
+ modifier: Modifier = Modifier,
+ horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+ verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+) {
+ Row(
+ modifier = modifier,
+ horizontalArrangement = horizontalArrangement,
+ verticalAlignment = verticalAlignment,
+ ) {
+ Box(
+ modifier = Modifier
+ .size(iconSize)
+ .align(Alignment.CenterVertically)
+ .background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape)
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ PlaceholderAtom(width = 20.dp, height = 7.dp)
+ Spacer(modifier = Modifier.width(7.dp))
+ PlaceholderAtom(width = 45.dp, height = 7.dp)
+ }
+}
+
+@DayNightPreviews
+@Composable
+internal fun IconTitlePlaceholdersRowMoleculePreview() = ElementPreview {
+ IconTitlePlaceholdersRowMolecule(
+ iconSize = AvatarSize.TimelineRoom.dp,
+ )
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt
index cc1d92ccd8..4856d54bff 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt
@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -39,6 +40,7 @@ fun LabelledTextField(
placeholder: String? = null,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
onValueChange: (String) -> Unit = {},
) {
Column(
@@ -59,17 +61,18 @@ fun LabelledTextField(
onValueChange = onValueChange,
singleLine = singleLine,
maxLines = maxLines,
+ keyboardOptions = keyboardOptions,
)
}
}
@Preview
@Composable
-fun LabelledTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() }
+internal fun LabelledTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
-fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() }
+internal fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt
index c6af3e1cdf..93a0c5d436 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt
@@ -51,6 +51,6 @@ fun PinIcon(
@DayNightPreviews
@Composable
-fun PinIconPreview() = ElementPreview {
+internal fun PinIconPreview() = ElementPreview {
PinIcon()
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt
index a5ad996ea7..20589c89ee 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt
@@ -35,7 +35,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
-import io.element.android.libraries.designsystem.components.dialogs.DialogPreview
+import io.element.android.libraries.designsystem.theme.components.DialogPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
@@ -129,9 +129,10 @@ private fun ProgressDialogContent(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.BottomEnd
) {
- TextButton(onClick = onCancelClicked) {
- Text(stringResource(id = CommonStrings.action_cancel))
- }
+ TextButton(
+ text = stringResource(id = CommonStrings.action_cancel),
+ onClick = onCancelClicked,
+ )
}
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt
index b5aa9b85d3..d30b78ca7f 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt
@@ -48,9 +48,10 @@ fun AsyncFailure(
Text(text = throwable.message ?: stringResource(id = CommonStrings.error_unknown))
if (onRetry != null) {
Spacer(modifier = Modifier.height(24.dp))
- Button(onClick = onRetry) {
- Text(text = stringResource(id = CommonStrings.action_retry))
- }
+ Button(
+ text = stringResource(id = CommonStrings.action_retry),
+ onClick = onRetry
+ )
}
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt
index b28e52a5ff..1c763a81c3 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt
@@ -102,7 +102,7 @@ private fun InitialsAvatar(
@Preview(group = PreviewGroup.Avatars)
@Composable
-fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) =
+internal fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) =
ElementThemedPreview {
Row(
verticalAlignment = Alignment.CenterVertically,
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt
index 1727fffd1c..14c7ad3eff 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt
@@ -20,19 +20,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class AvatarDataProvider : PreviewParameterProvider {
override val values: Sequence
- get() {
- AvatarSize.values()
- .also { it.sortBy { item -> item.name } }
- .asSequence()
- return AvatarSize.values().asSequence().map {
+ get() = AvatarSize.values()
+ .asSequence()
+ .map {
sequenceOf(
anAvatarData(size = it),
anAvatarData(size = it).copy(name = null),
anAvatarData(size = it).copy(url = "aUrl"),
)
}
- .flatten()
- }
+ .flatten()
}
fun anAvatarData(
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt
new file mode 100644
index 0000000000..24f3989f66
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.components.button
+
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.IconButton
+import io.element.android.libraries.designsystem.theme.components.IconSource
+import io.element.android.libraries.designsystem.theme.components.TextButton
+
+/**
+ * A sealed class that represents the different visual styles that a button can have.
+ */
+sealed interface ButtonVisuals {
+
+ val action: () -> Unit
+
+ /**
+ * Creates a [Button] composable based on the visual state.
+ */
+ @Composable
+ fun Composable()
+
+ data class Text(val text: String, override val action: () -> Unit) : ButtonVisuals {
+ @Composable
+ override fun Composable() {
+ TextButton(text = text, onClick = action)
+ }
+ }
+ data class Icon(val iconSource: IconSource, override val action: () -> Unit) : ButtonVisuals {
+ @Composable
+ override fun Composable() {
+ IconButton(onClick = action) {
+ Icon(iconSource.getPainter(), iconSource.contentDescription)
+ }
+ }
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt
deleted file mode 100644
index 06702de253..0000000000
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.libraries.designsystem.components.button
-
-import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.progressSemantics
-import androidx.compose.material3.ButtonColors
-import androidx.compose.material3.ButtonElevation
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.ElementThemedPreview
-import io.element.android.libraries.designsystem.preview.PreviewGroup
-import io.element.android.libraries.designsystem.theme.aliasButtonText
-import io.element.android.libraries.designsystem.theme.components.Button
-import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
-import io.element.android.libraries.designsystem.theme.components.ElementButtonDefaults
-import io.element.android.libraries.designsystem.theme.components.Text
-import io.element.android.libraries.theme.ElementTheme
-
-/**
- * A component that will display a button with an indeterminate circular progressbar.
- * When [showProgress] is true:
- * - A circular progressbar is displayed.
- * - [text] is replaced by [progressText], if defined.
- * - [onClick] gets disabled.
- */
-@Composable
-fun ButtonWithProgress(
- text: String?,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- showProgress: Boolean = false,
- progressText: String? = text,
- enabled: Boolean = true,
- shape: Shape = ElementButtonDefaults.shape,
- colors: ButtonColors = ElementButtonDefaults.buttonColors(),
- elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(),
- border: BorderStroke? = null,
- contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-) {
- Button(
- onClick = {
- if (!showProgress) {
- onClick()
- }
- },
- modifier = modifier,
- enabled = enabled,
- shape = shape,
- colors = colors,
- elevation = elevation,
- border = border,
- contentPadding = contentPadding,
- interactionSource = interactionSource,
- ) {
- if (showProgress) {
- CircularProgressIndicator(
- modifier = Modifier
- .progressSemantics()
- .size(18.dp),
- color = MaterialTheme.colorScheme.onPrimary,
- strokeWidth = 2.dp,
- )
- if (progressText != null) {
- Spacer(Modifier.width(10.dp))
- Text(progressText, style = ElementTheme.typography.aliasButtonText)
- }
- } else if (text != null) {
- Text(text, style = ElementTheme.typography.aliasButtonText)
- }
- }
-}
-
-@Preview(group = PreviewGroup.Buttons)
-@Composable
-internal fun ButtonWithProgressPreview() = ElementThemedPreview {
- ButtonWithProgress(
- text = "Button with progress",
- onClick = {},
- showProgress = true,
- )
-}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt
index b11c9f878d..0c2fca84d5 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt
@@ -17,20 +17,15 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
-import io.element.android.libraries.designsystem.utils.BooleanProvider
+import io.element.android.libraries.designsystem.theme.components.DialogPreview
+import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@@ -44,16 +39,8 @@ fun ConfirmationDialog(
submitText: String = stringResource(id = CommonStrings.action_ok),
cancelText: String = stringResource(id = CommonStrings.action_cancel),
thirdButtonText: String? = null,
- emphasizeSubmitButton: Boolean = false,
onCancelClicked: () -> Unit = onDismiss,
onThirdButtonClicked: () -> Unit = {},
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- // According to the design team, `primary` should be used here instead of the default `onSurface`
- titleContentColor: Color = MaterialTheme.colorScheme.primary,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
AlertDialog(modifier = modifier, onDismissRequest = onDismiss) {
ConfirmationDialogContent(
@@ -65,13 +52,6 @@ fun ConfirmationDialog(
onSubmitClicked = onSubmitClicked,
onCancelClicked = onCancelClicked,
onThirdButtonClicked = onThirdButtonClicked,
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
- emphasizeSubmitButton = emphasizeSubmitButton,
)
}
}
@@ -87,13 +67,6 @@ private fun ConfirmationDialogContent(
title: String? = null,
thirdButtonText: String? = null,
onThirdButtonClicked: () -> Unit = {},
- emphasizeSubmitButton: Boolean = false,
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- titleContentColor: Color = AlertDialogDefaults.titleContentColor,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
icon: @Composable (() -> Unit)? = null,
) {
SimpleAlertDialogContent(
@@ -106,21 +79,14 @@ private fun ConfirmationDialogContent(
onCancelClicked = onCancelClicked,
thirdButtonText = thirdButtonText,
onThirdButtonClicked = onThirdButtonClicked,
- emphasizeSubmitButton = emphasizeSubmitButton,
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
icon = icon,
)
}
@Preview(group = PreviewGroup.Dialogs)
@Composable
-internal fun ConfirmationDialogPreview(@PreviewParameter(BooleanProvider::class) emphasizeSubmitButton: Boolean) =
- ElementThemedPreview {
+internal fun ConfirmationDialogPreview() =
+ ElementThemedPreview(showBackground = false) {
DialogPreview {
ConfirmationDialogContent(
content = "Content",
@@ -130,7 +96,7 @@ internal fun ConfirmationDialogPreview(@PreviewParameter(BooleanProvider::class)
thirdButtonText = "Disable",
onSubmitClicked = {},
onCancelClicked = {},
- emphasizeSubmitButton = emphasizeSubmitButton,
+ onThirdButtonClicked = {},
)
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt
index d69281b6e9..fb5c511bf4 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt
@@ -17,17 +17,15 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.theme.components.DialogPreview
+import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@@ -38,12 +36,6 @@ fun ErrorDialog(
title: String = ErrorDialogDefaults.title,
submitText: String = ErrorDialogDefaults.submitText,
onDismiss: () -> Unit = {},
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- titleContentColor: Color = AlertDialogDefaults.titleContentColor,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
AlertDialog(modifier = modifier, onDismissRequest = onDismiss) {
ErrorDialogContent(
@@ -51,12 +43,6 @@ fun ErrorDialog(
content = content,
submitText = submitText,
onSubmitText = onDismiss,
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
)
}
}
@@ -68,12 +54,6 @@ private fun ErrorDialogContent(
title: String = ErrorDialogDefaults.title,
submitText: String = ErrorDialogDefaults.submitText,
onSubmitText: () -> Unit = {},
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- titleContentColor: Color = AlertDialogDefaults.titleContentColor,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
SimpleAlertDialogContent(
modifier = modifier,
@@ -81,12 +61,6 @@ private fun ErrorDialogContent(
content = content,
cancelText = submitText,
onCancelClicked = onSubmitText,
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
)
}
@@ -98,7 +72,7 @@ object ErrorDialogDefaults {
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun ErrorDialogPreview() {
- ElementThemedPreview {
+ ElementThemedPreview(showBackground = false) {
DialogPreview {
ErrorDialogContent(
content = "Content",
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt
index 5e22779085..85447b940b 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt
@@ -17,21 +17,18 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.AlertDialogDefaults
-import androidx.compose.material3.TextButton
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
-import io.element.android.libraries.designsystem.theme.components.Text
-import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.designsystem.theme.components.DialogPreview
+import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RetryDialog(
content: String,
@@ -41,51 +38,17 @@ fun RetryDialog(
dismissText: String = RetryDialogDefaults.dismissText,
onRetry: () -> Unit = {},
onDismiss: () -> Unit = {},
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- titleContentColor: Color = AlertDialogDefaults.titleContentColor,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
- AlertDialog(
- modifier = modifier,
- onDismissRequest = onDismiss,
- title = {
- Text(
- text = title,
- style = ElementTheme.typography.fontHeadingSmRegular,
- )
- },
- text = {
- Text(
- text = content,
- style = ElementTheme.typography.fontBodyMdRegular,
- )
- },
- confirmButton = {
- TextButton(onClick = onRetry) {
- Text(
- text = retryText,
- style = ElementTheme.typography.fontBodyMdRegular,
- )
- }
- },
- dismissButton = {
- TextButton(onClick = onDismiss) {
- Text(
- text = dismissText,
- style = ElementTheme.typography.fontBodyMdRegular,
- )
- }
- },
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
- )
+ AlertDialog(modifier = modifier, onDismissRequest = onDismiss) {
+ RetryDialogContent(
+ title = title,
+ content = content,
+ retryText = retryText,
+ dismissText = dismissText,
+ onRetry = onRetry,
+ onDismiss = onDismiss,
+ )
+ }
}
@Composable
@@ -97,12 +60,6 @@ private fun RetryDialogContent(
dismissText: String = RetryDialogDefaults.dismissText,
onRetry: () -> Unit = {},
onDismiss: () -> Unit = {},
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- titleContentColor: Color = AlertDialogDefaults.titleContentColor,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
) {
SimpleAlertDialogContent(
modifier = modifier,
@@ -112,12 +69,6 @@ private fun RetryDialogContent(
onSubmitClicked = onRetry,
cancelText = dismissText,
onCancelClicked = onDismiss,
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
)
}
@@ -129,13 +80,12 @@ object RetryDialogDefaults {
@Preview(group = PreviewGroup.Dialogs)
@Composable
-internal fun RetryDialogPreview() = ElementThemedPreview { ContentToPreview() }
-
-@Composable
-private fun ContentToPreview() {
- DialogPreview {
- RetryDialogContent(
- content = "Content",
- )
+internal fun RetryDialogPreview() {
+ ElementThemedPreview(showBackground = false) {
+ DialogPreview {
+ RetryDialogContent(
+ content = "Content",
+ )
+ }
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt
index bbd3583688..b9a8d267f5 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Announcement
-import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -37,6 +36,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.theme.components.Switch
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt
index 201d6f7151..b91e6a1024 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt
@@ -42,6 +42,13 @@ const val DAY_MODE_NAME = "D"
*
* NB: Content should be wrapped into [ElementPreview] to apply proper theming.
*/
-@Preview(name = DAY_MODE_NAME)
-@Preview(name = NIGHT_MODE_NAME, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Preview(
+ name = DAY_MODE_NAME,
+ fontScale = 1f,
+)
+@Preview(
+ name = NIGHT_MODE_NAME,
+ uiMode = Configuration.UI_MODE_NIGHT_YES,
+ fontScale = 1f,
+)
annotation class DayNightPreviews
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt
index aaff9a49f9..b704d0a26e 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt
@@ -30,6 +30,7 @@ object PreviewGroup {
const val Preferences = "Preferences"
const val Progress = "Progress Indicators"
const val Search = "Search views"
+ const val Snackbars = "Snackbars"
const val Sliders = "Sliders"
const val Text = "Text"
const val TextFields = "TextFields"
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt
new file mode 100644
index 0000000000..a83bc5708c
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.preview
+
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.SheetState
+import androidx.compose.material3.SheetValue
+
+@OptIn(ExperimentalMaterial3Api::class)
+val sheetStateForPreview = SheetState(
+ skipPartiallyExpanded = true,
+ initialValue = SheetValue.Expanded,
+)
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt
new file mode 100644
index 0000000000..6d3ecfc82b
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.preview
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
+
+/**
+ * Showkase does not take into account the `fontScale` parameter of the Preview annotation, so alter the
+ * LocalDensity in the CompositionLocalProvider.
+ */
+@Composable
+fun WithFontScale(fontScale: Float, content: @Composable () -> Unit) {
+ CompositionLocalProvider(
+ LocalDensity provides Density(
+ density = LocalDensity.current.density,
+ fontScale = fontScale
+ )
+ ) {
+ content()
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
index a5fc895e5d..58c9a44709 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt
@@ -24,8 +24,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
-import io.element.android.libraries.designsystem.theme.components.Text
/**
* Debug tool to add a vertical and a horizontal ruler on top of the content.
@@ -76,8 +76,10 @@ internal fun WithRulerDarkPreview() =
@Composable
private fun ContentToPreview() {
WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) {
- OutlinedButton(onClick = {}) {
- Text(text = "A Button with rulers on it!")
- }
+ OutlinedButton(
+ text = "A Button with rulers on it!",
+ size = ButtonSize.Medium,
+ onClick = {},
+ )
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt
new file mode 100644
index 0000000000..c6408b662e
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.text
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.preview.WithFontScale
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.theme.ElementTheme
+
+/**
+ * Return the maximum value between the receiver value and the value with fontScale applied.
+ * So if fontScale is >= 1f, the same value is returned, and if fontScale is < 1f, so returned value
+ * will be smaller.
+ */
+@Composable
+fun Dp.applyScaleDown(): Dp = with(LocalDensity.current) {
+ return this@applyScaleDown * fontScale.coerceAtMost(1f)
+}
+
+/**
+ * Return the minimum value between the receiver value and the value with fontScale applied.
+ * So if fontScale is <= 1f, the same value is returned, and if fontScale is > 1f, so returned value
+ * will be bigger.
+ */
+@Composable
+fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) {
+ return this@applyScaleUp * fontScale.coerceAtLeast(1f)
+}
+
+@Preview
+@Composable
+internal fun DpScalePreview_0_75f() = WithFontScale(0.75f) {
+ ElementPreviewLight {
+ val fontSizeInDp = 16.dp
+ Column(
+ modifier = Modifier.padding(4.dp),
+ verticalArrangement = Arrangement.spacedBy(6.dp)
+ ) {
+ Text(
+ text = "Text with size of 16.sp",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp())
+ )
+ Text(
+ text = "Text with the same size (applyScaleUp)",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp())
+ )
+ Text(
+ text = "Text with a smaller size (applyScaleDown)",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp())
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+internal fun DpScalePreview_1_0f() = WithFontScale(1f) {
+ ElementPreviewLight {
+ val fontSizeInDp = 16.dp
+ Column(
+ modifier = Modifier.padding(4.dp),
+ verticalArrangement = Arrangement.spacedBy(6.dp)
+ ) {
+ Text(
+ text = "Text with size of 16.sp",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp())
+ )
+ Text(
+ text = "Text with the same size (applyScaleUp)",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp())
+ )
+ Text(
+ text = "Text with the same size (applyScaleDown)",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp())
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+internal fun DpScalePreview_1_5f() = WithFontScale(1.5f) {
+ ElementPreviewLight {
+ val fontSizeInDp = 16.dp
+ Column(
+ modifier = Modifier.padding(4.dp),
+ verticalArrangement = Arrangement.spacedBy(6.dp)
+ ) {
+ Text(
+ text = "Text with size of 16.sp",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp())
+ )
+ Text(
+ text = "Text with a bigger size (applyScaleUp)",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp())
+ )
+ Text(
+ text = "Text with the same size (applyScaleDown)",
+ style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp())
+ )
+ }
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
similarity index 64%
rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt
rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
index c1e8b0c055..a3c7274c45 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.designsystem.components.dialogs
+package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -22,26 +22,32 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
-import androidx.compose.material3.AlertDialogDefaults
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Surface
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.preview.ElementThemedPreview
+import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
import kotlin.math.max
+// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=911%3A343492&mode=design&t=jeyd1bXKOOx8y10r-1
+
@Composable
internal fun SimpleAlertDialogContent(
content: String,
@@ -53,13 +59,6 @@ internal fun SimpleAlertDialogContent(
onSubmitClicked: () -> Unit = {},
thirdButtonText: String? = null,
onThirdButtonClicked: () -> Unit = {},
- emphasizeSubmitButton: Boolean = false,
- shape: Shape = AlertDialogDefaults.shape,
- containerColor: Color = AlertDialogDefaults.containerColor,
- iconContentColor: Color = AlertDialogDefaults.iconContentColor,
- titleContentColor: Color = AlertDialogDefaults.titleContentColor,
- textContentColor: Color = AlertDialogDefaults.textContentColor,
- tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
icon: @Composable (() -> Unit)? = null,
) {
AlertDialogContent(
@@ -71,54 +70,47 @@ internal fun SimpleAlertDialogContent(
if (thirdButtonText != null) {
// If there is a 3rd item it should be at the end of the dialog
// Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f
- TextButton(onClick = onThirdButtonClicked) {
- Text(
- text = thirdButtonText,
- style = ElementTheme.typography.fontBodyMdRegular,
- )
- }
- }
- TextButton(onClick = onCancelClicked) {
- Text(
- text = cancelText,
- style = ElementTheme.typography.fontBodyMdRegular,
+ TextButton(
+ text = thirdButtonText,
+ size = ButtonSize.Medium,
+ onClick = onThirdButtonClicked,
)
}
+ TextButton(
+ text = cancelText,
+ size = ButtonSize.Medium,
+ onClick = onCancelClicked,
+ )
if (submitText != null) {
- TextButton(onClick = onSubmitClicked) {
- Text(
- text = submitText,
- style = if (emphasizeSubmitButton) {
- ElementTheme.typography.fontBodyMdMedium
- } else {
- ElementTheme.typography.fontBodyMdRegular
- }
- )
- }
+ Button(
+ text = submitText,
+ size = ButtonSize.Medium,
+ onClick = onSubmitClicked,
+ )
}
}
},
modifier = modifier,
- title = {
- if (title != null) {
+ title = title?.let { titleText ->
+ @Composable {
Text(
- text = title,
- style = ElementTheme.typography.fontHeadingSmRegular,
+ text = titleText,
+ style = ElementTheme.typography.fontHeadingSmMedium,
)
}
},
text = {
Text(
text = content,
- style = ElementTheme.typography.fontBodyMdRegular,
+ style = ElementTheme.materialTypography.bodyMedium,
)
},
- shape = shape,
- containerColor = containerColor,
- iconContentColor = iconContentColor,
- titleContentColor = titleContentColor,
- textContentColor = textContentColor,
- tonalElevation = tonalElevation,
+ shape = DialogContentDefaults.shape,
+ containerColor = DialogContentDefaults.containerColor,
+ iconContentColor = DialogContentDefaults.iconContentColor,
+ titleContentColor = DialogContentDefaults.titleContentColor,
+ textContentColor = DialogContentDefaults.textContentColor,
+ tonalElevation = 0.dp,
icon = icon,
// Note that a button content color is provided here from the dialog's token, but in
// most cases, TextButtons should be used for dismiss and confirm buttons.
@@ -128,6 +120,9 @@ internal fun SimpleAlertDialogContent(
)
}
+/**
+ * Copy of M3's `AlertDialogContent` so we can use it for previews.
+ */
@Composable
internal fun AlertDialogContent(
buttons: @Composable () -> Unit,
@@ -150,13 +145,13 @@ internal fun AlertDialogContent(
tonalElevation = tonalElevation,
) {
Column(
- modifier = Modifier.padding(DialogPadding)
+ modifier = Modifier.padding(DialogContentDefaults.externalPadding)
) {
icon?.let {
CompositionLocalProvider(LocalContentColor provides iconContentColor) {
Box(
Modifier
- .padding(IconPadding)
+ .padding(DialogContentDefaults.iconPadding)
.align(Alignment.CenterHorizontally)
) {
icon()
@@ -170,7 +165,7 @@ internal fun AlertDialogContent(
Box(
// Align the title to the center when an icon is present.
Modifier
- .padding(TitlePadding)
+ .padding(DialogContentDefaults.titlePadding)
.align(
if (icon == null) {
Alignment.Start
@@ -192,7 +187,7 @@ internal fun AlertDialogContent(
Box(
Modifier
.weight(weight = 1f, fill = false)
- .padding(TextPadding)
+ .padding(DialogContentDefaults.textPadding)
.align(Alignment.Start)
) {
text()
@@ -216,7 +211,7 @@ internal fun AlertDialogContent(
* customization.
*/
@Composable
-internal fun AlertDialogFlowRow(
+private fun AlertDialogFlowRow(
mainAxisSpacing: Dp,
crossAxisSpacing: Dp,
content: @Composable () -> Unit
@@ -243,7 +238,8 @@ internal fun AlertDialogFlowRow(
if (sequences.isNotEmpty()) {
crossAxisSpace += crossAxisSpacing.roundToPx()
}
- sequences += currentSequence.toList()
+ // Ensures that confirming actions appear above dismissive actions.
+ sequences.add(0, currentSequence.toList())
crossAxisSizes += currentCrossAxisSize
crossAxisPositions += crossAxisSpace
@@ -287,12 +283,11 @@ internal fun AlertDialogFlowRow(
placeables[j].width +
if (j < placeables.lastIndex) mainAxisSpacing.roundToPx() else 0
}
- val arrangement = Arrangement.Bottom
- // TODO(soboleva): rtl support
- // Handle vertical direction
+ val arrangement = Arrangement.End
val mainAxisPositions = IntArray(childrenMainAxisSizes.size) { 0 }
with(arrangement) {
- arrange(mainAxisLayoutSize, childrenMainAxisSizes, mainAxisPositions)
+ arrange(mainAxisLayoutSize, childrenMainAxisSizes,
+ layoutDirection, mainAxisPositions)
}
placeables.forEachIndexed { j, placeable ->
placeable.place(
@@ -317,14 +312,87 @@ internal fun DialogPreview(content: @Composable () -> Unit) {
}
}
-// Paddings for each of the dialog's parts.
-private val DialogPadding = PaddingValues(all = 24.dp)
-private val IconPadding = PaddingValues(bottom = 16.dp)
-private val TitlePadding = PaddingValues(bottom = 16.dp)
-private val TextPadding = PaddingValues(bottom = 24.dp)
+internal object DialogContentDefaults {
+ val shape = RoundedCornerShape(12.dp)
+ val externalPadding = PaddingValues(all = 24.dp)
+ val titlePadding = PaddingValues(bottom = 16.dp)
+ val iconPadding = PaddingValues(bottom = 8.dp)
+ val textPadding = PaddingValues(bottom = 16.dp)
+ val containerColor: Color
+ @Composable
+ @ReadOnlyComposable
+ get()= ElementTheme.colors.bgCanvasDefault
+
+ val textContentColor: Color
+ @Composable
+ @ReadOnlyComposable
+ get()= ElementTheme.materialColors.onSurfaceVariant
+
+ val titleContentColor: Color
+ @Composable
+ @ReadOnlyComposable
+ get()= ElementTheme.materialColors.onSurface
+
+ val iconContentColor: Color
+ @Composable
+ @ReadOnlyComposable
+ get()= ElementTheme.materialColors.primary
+}
+
+// Paddings for each of the dialog's parts. Taken from M3 source code.
internal val ButtonsMainAxisSpacing = 8.dp
internal val ButtonsCrossAxisSpacing = 12.dp
internal val DialogMinWidth = 280.dp
internal val DialogMaxWidth = 560.dp
+
+@Preview(group = PreviewGroup.Dialogs, name = "Dialog with title, icon and ok button")
+@Composable
+@Suppress("MaxLineLength")
+internal fun DialogWithTitleIconAndOkButtonPreview() {
+ ElementThemedPreview(showBackground = false) {
+ DialogPreview {
+ SimpleAlertDialogContent(
+ icon = {
+ Icon(imageVector = Icons.Default.Notifications, contentDescription = null)
+ },
+ title = "Dialog Title",
+ content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more",
+ cancelText = "OK",
+ onCancelClicked = {},
+ )
+ }
+ }
+}
+
+@Preview(group = PreviewGroup.Dialogs, name = "Dialog with title and ok button")
+@Composable
+@Suppress("MaxLineLength")
+internal fun DialogWithTitleAndOkButtonPreview() {
+ ElementThemedPreview(showBackground = false) {
+ DialogPreview {
+ SimpleAlertDialogContent(
+ title = "Dialog Title",
+ content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more",
+ cancelText = "OK",
+ onCancelClicked = {},
+ )
+ }
+ }
+}
+
+@Preview(group = PreviewGroup.Dialogs, name = "Dialog with only message and ok button")
+@Composable
+@Suppress("MaxLineLength")
+internal fun DialogWithOnlyMessageAndOkButtonPreview() {
+ ElementThemedPreview(showBackground = false) {
+ DialogPreview {
+ SimpleAlertDialogContent(
+ content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more",
+ cancelText = "OK",
+ onCancelClicked = {},
+ )
+ }
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
index 64c79a3906..8c5d96c400 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt
@@ -18,68 +18,367 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.progressSemantics
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.ButtonElevation
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+
+// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=U03tOFZz5FSLVUMa-1
@Composable
fun Button(
+ text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
- shape: Shape = ElementButtonDefaults.shape,
- colors: ButtonColors = ElementButtonDefaults.buttonColors(),
- elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(),
- border: BorderStroke? = null,
- contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- content: @Composable RowScope.() -> Unit
+ size: ButtonSize = ButtonSize.Large,
+ showProgress: Boolean = false,
+ leadingIcon: IconSource? = null,
+) = ButtonInternal(
+ text = text,
+ onClick = onClick,
+ style = ButtonStyle.Filled,
+ modifier = modifier,
+ enabled = enabled,
+ size = size,
+ showProgress = showProgress,
+ leadingIcon = leadingIcon
+)
+
+@Composable
+fun OutlinedButton(
+ text: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ size: ButtonSize = ButtonSize.Large,
+ showProgress: Boolean = false,
+ leadingIcon: IconSource? = null,
+) = ButtonInternal(
+ text = text,
+ onClick = onClick,
+ style = ButtonStyle.Outlined,
+ modifier = modifier,
+ enabled = enabled,
+ size = size,
+ showProgress = showProgress,
+ leadingIcon = leadingIcon
+)
+
+@Composable
+fun TextButton(
+ text: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ size: ButtonSize = ButtonSize.Large,
+ showProgress: Boolean = false,
+ leadingIcon: IconSource? = null,
+) = ButtonInternal(
+ text = text,
+ onClick = onClick,
+ style = ButtonStyle.Text,
+ modifier = modifier,
+ enabled = enabled,
+ size = size,
+ showProgress = showProgress,
+ leadingIcon = leadingIcon
+)
+
+@Composable
+internal fun ButtonInternal(
+ text: String,
+ onClick: () -> Unit,
+ style: ButtonStyle,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = style.getColors(),
+ enabled: Boolean = true,
+ size: ButtonSize = ButtonSize.Large,
+ showProgress: Boolean = false,
+ leadingIcon: IconSource? = null,
) {
+ val minHeight = when (size) {
+ ButtonSize.Medium -> 40.dp
+ ButtonSize.Large -> 48.dp
+ }
+
+ val contentPadding = when (size) {
+ ButtonSize.Medium -> {
+ when (style) {
+ ButtonStyle.Text -> PaddingValues(horizontal = 12.dp, vertical = 10.dp)
+ else -> PaddingValues(horizontal = 16.dp, vertical = 10.dp)
+ }
+ }
+ ButtonSize.Large -> {
+ when (style) {
+ ButtonStyle.Text -> PaddingValues(horizontal = 16.dp, vertical = 13.dp)
+ else -> PaddingValues(horizontal = 24.dp, vertical = 13.dp)
+ }
+ }
+ }
+
+ val shape = when (style) {
+ ButtonStyle.Filled, ButtonStyle.Outlined -> RoundedCornerShape(percent = 50)
+ ButtonStyle.Text -> RectangleShape
+ }
+
+ val border = when (style) {
+ ButtonStyle.Filled, ButtonStyle.Text -> null
+ ButtonStyle.Outlined -> BorderStroke(
+ width = 1.dp,
+ color = ElementTheme.colors.borderInteractiveSecondary
+ )
+ }
+
+ val textStyle = when (size) {
+ ButtonSize.Medium -> MaterialTheme.typography.labelLarge
+ ButtonSize.Large -> ElementTheme.typography.fontBodyLgMedium
+ }
+
+ val internalPadding = when {
+ style == ButtonStyle.Text -> if (leadingIcon != null) PaddingValues(start = 8.dp) else PaddingValues(0.dp)
+ else -> PaddingValues(horizontal = 8.dp)
+ }
+
androidx.compose.material3.Button(
- onClick = onClick,
- modifier = modifier,
+ onClick = {
+ if (!showProgress) {
+ onClick()
+ }
+ },
+ modifier = modifier.heightIn(min = minHeight),
enabled = enabled,
shape = shape,
colors = colors,
- elevation = elevation,
+ elevation = null,
border = border,
contentPadding = contentPadding,
- interactionSource = interactionSource,
- content = content,
- )
+ interactionSource = remember { MutableInteractionSource() },
+ ) {
+ when {
+ showProgress -> {
+ CircularProgressIndicator(
+ modifier = Modifier
+ .progressSemantics()
+ .size(20.dp),
+ color = LocalContentColor.current,
+ strokeWidth = 2.dp,
+ )
+ }
+ leadingIcon != null -> {
+ androidx.compose.material.Icon(
+ painter = leadingIcon.getPainter(),
+ contentDescription = null,
+ tint = LocalContentColor.current,
+ modifier = Modifier.size(20.dp),
+ )
+ }
+ else -> Unit
+ }
+ Text(
+ text = text,
+ style = textStyle,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(internalPadding),
+ )
+ }
}
-object ElementButtonDefaults {
- val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp)
- val shape: Shape @Composable get() = ButtonDefaults.shape
- @Composable
- fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation()
+sealed interface IconSource {
+ val contentDescription: String?
+
+ data class Resource(val id: Int, override val contentDescription: String? = null) : IconSource
+ data class Vector(val vector: ImageVector, override val contentDescription: String? = null) : IconSource
@Composable
- fun buttonColors(): ButtonColors = ButtonDefaults.buttonColors()
+ fun getPainter(): Painter = when (this) {
+ is Resource -> painterResource(id)
+ is Vector -> rememberVectorPainter(image = vector)
+ }
+}
+enum class ButtonSize {
+ Medium, Large
+}
+
+internal enum class ButtonStyle {
+ Filled, Outlined, Text;
+
+ @Composable
+ fun getColors(): ButtonColors = when (this) {
+ Filled -> ButtonDefaults.buttonColors(
+ containerColor = ElementTheme.materialColors.primary,
+ contentColor = ElementTheme.materialColors.onPrimary,
+ disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled,
+ disabledContentColor = ElementTheme.colors.textOnSolidPrimary
+ )
+ Outlined -> ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = ElementTheme.materialColors.primary,
+ disabledContainerColor = Color.Transparent,
+ disabledContentColor = ElementTheme.colors.textDisabled,
+ )
+ Text -> ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = if (LocalContentColor.current.isSpecified) LocalContentColor.current else ElementTheme.materialColors.primary,
+ disabledContainerColor = Color.Transparent,
+ disabledContentColor = ElementTheme.colors.textDisabled,
+ )
+ }
}
@Preview(group = PreviewGroup.Buttons)
@Composable
-internal fun ButtonPreview() = ElementThemedPreview {
- Column {
- Button(onClick = {}, enabled = true) {
- Text(text = "Click me! - Enabled")
- }
- Button(onClick = {}, enabled = false) {
- Text(text = "Click me! - Disabled")
+internal fun FilledButtonMediumPreview() {
+ ButtonCombinationPreview(
+ style = ButtonStyle.Filled,
+ size = ButtonSize.Medium,
+ )
+}
+
+@Preview(group = PreviewGroup.Buttons)
+@Composable
+internal fun FilledButtonLargePreview() {
+ ButtonCombinationPreview(
+ style = ButtonStyle.Filled,
+ size = ButtonSize.Large,
+ )
+}
+
+@Preview(group = PreviewGroup.Buttons)
+@Composable
+internal fun OutlinedButtonMediumPreview() {
+ ButtonCombinationPreview(
+ style = ButtonStyle.Outlined,
+ size = ButtonSize.Medium,
+ )
+}
+
+@Preview(group = PreviewGroup.Buttons)
+@Composable
+internal fun OutlinedButtonLargePreview() {
+ ButtonCombinationPreview(
+ style = ButtonStyle.Outlined,
+ size = ButtonSize.Large,
+ )
+}
+
+@Preview(group = PreviewGroup.Buttons)
+@Composable
+internal fun TextButtonMediumPreview() {
+ ButtonCombinationPreview(
+ style = ButtonStyle.Text,
+ size = ButtonSize.Medium,
+ )
+}
+
+@Preview(group = PreviewGroup.Buttons)
+@Composable
+internal fun TextButtonLargePreview() {
+ ButtonCombinationPreview(
+ style = ButtonStyle.Text,
+ size = ButtonSize.Large,
+ )
+}
+
+@Composable
+private fun ButtonCombinationPreview(
+ style: ButtonStyle,
+ size: ButtonSize,
+ modifier: Modifier = Modifier,
+) {
+ ElementThemedPreview {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ modifier = Modifier
+ .padding(16.dp)
+ .width(IntrinsicSize.Max),
+ ) {
+ // Normal
+ ButtonRowPreview(
+ modifier = Modifier.then(modifier),
+ style = style,
+ size = size,
+ )
+
+ // With icon
+ ButtonRowPreview(
+ modifier = Modifier.then(modifier),
+ leadingIcon = IconSource.Vector(Icons.Outlined.Share),
+ style = style,
+ size = size,
+ )
+
+ // With progress
+ ButtonRowPreview(
+ modifier = Modifier.then(modifier),
+ showProgress = true,
+ style = style,
+ size = size,
+ )
}
}
}
+
+@Composable
+private fun ButtonRowPreview(
+ style: ButtonStyle,
+ size: ButtonSize,
+ modifier: Modifier = Modifier,
+ leadingIcon: IconSource? = null,
+ showProgress: Boolean = false,
+) {
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)) {
+ ButtonInternal(
+ text = "A button",
+ showProgress = showProgress,
+ onClick = {},
+ style = style,
+ size = size,
+ leadingIcon = leadingIcon,
+ modifier = Modifier.then(modifier),
+ )
+ ButtonInternal(
+ text = "A button",
+ showProgress = showProgress,
+ enabled = false,
+ onClick = {},
+ style = style,
+ size = size,
+ leadingIcon = leadingIcon,
+ modifier = Modifier.then(modifier),
+ )
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt
index b754b3d420..31c6cc3647 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt
@@ -17,15 +17,25 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.material3.CheckboxColors
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+
+// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=qb99xBP5mwwCtGkN-1
@Composable
fun Checkbox(
@@ -33,12 +43,20 @@ fun Checkbox(
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
- colors: CheckboxColors = CheckboxDefaults.colors(),
+ hasError: Boolean = false,
+ indeterminate: Boolean = false,
+ colors: CheckboxColors = if (hasError) compoundErrorCheckBoxColors() else compoundCheckBoxColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
- androidx.compose.material3.Checkbox(
- checked = checked,
- onCheckedChange = onCheckedChange,
+ var indeterminateState by remember { mutableStateOf(indeterminate) }
+ androidx.compose.material3.TriStateCheckbox(
+ state = if (!checked && indeterminateState) ToggleableState.Indeterminate else ToggleableState(checked),
+ onClick = onCheckedChange?.let {
+ {
+ indeterminateState = false
+ onCheckedChange(!checked)
+ }
+ },
modifier = modifier,
enabled = enabled,
colors = colors,
@@ -46,6 +64,30 @@ fun Checkbox(
)
}
+@Composable
+private fun compoundCheckBoxColors(): CheckboxColors {
+ return CheckboxDefaults.colors(
+ checkedColor = ElementTheme.materialColors.primary,
+ uncheckedColor = ElementTheme.colors.borderInteractivePrimary,
+ checkmarkColor = ElementTheme.materialColors.onPrimary,
+ disabledUncheckedColor = ElementTheme.colors.borderDisabled,
+ disabledCheckedColor = ElementTheme.colors.iconDisabled,
+ disabledIndeterminateColor = ElementTheme.colors.iconDisabled,
+ )
+}
+
+@Composable
+private fun compoundErrorCheckBoxColors(): CheckboxColors {
+ return CheckboxDefaults.colors(
+ checkedColor = ElementTheme.materialColors.error,
+ uncheckedColor = ElementTheme.materialColors.error,
+ checkmarkColor = ElementTheme.materialColors.onPrimary,
+ disabledUncheckedColor = ElementTheme.colors.borderDisabled,
+ disabledCheckedColor = ElementTheme.colors.iconDisabled,
+ disabledIndeterminateColor = ElementTheme.colors.iconDisabled,
+ )
+}
+
@Preview(group = PreviewGroup.Toggles)
@Composable
internal fun CheckboxesPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() }
@@ -53,9 +95,33 @@ internal fun CheckboxesPreview() = ElementThemedPreview(vertical = false) { Cont
@Composable
private fun ContentToPreview() {
Column {
- Checkbox(onCheckedChange = {}, enabled = true, checked = true)
- Checkbox(onCheckedChange = {}, enabled = true, checked = false)
- Checkbox(onCheckedChange = {}, enabled = false, checked = true)
- Checkbox(onCheckedChange = {}, enabled = false, checked = false)
+ // Unchecked
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Checkbox(onCheckedChange = {}, enabled = true, checked = false)
+ Checkbox(onCheckedChange = {}, enabled = false, checked = false)
+ }
+ // Checked
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Checkbox(onCheckedChange = {}, enabled = true, checked = true)
+ Checkbox(onCheckedChange = {}, enabled = false, checked = true)
+ }
+ // Indeterminate
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Checkbox(onCheckedChange = {}, enabled = true, checked = false, indeterminate = true)
+ Checkbox(onCheckedChange = {}, enabled = false, checked = false, indeterminate = true)
+ }
+ // Error
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Checkbox(hasError = true, onCheckedChange = {}, checked = false)
+ Checkbox(hasError = true, onCheckedChange = {}, enabled = false, checked = false)
+ }
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Checkbox(hasError = true, onCheckedChange = {}, enabled = true, checked = true)
+ Checkbox(hasError = true, onCheckedChange = {}, enabled = false, checked = true)
+ }
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Checkbox(onCheckedChange = {}, enabled = true, checked = false, indeterminate = true, hasError = true)
+ Checkbox(onCheckedChange = {}, enabled = false, checked = false, indeterminate = true, hasError = true)
+ }
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt
index 175c0fb402..c0160bb7f8 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupProperties
import io.element.android.libraries.theme.ElementTheme
-private val minMenuWidth = 200.dp
+// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1
@Composable
fun DropdownMenu(
@@ -38,19 +38,17 @@ fun DropdownMenu(
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
) {
- val bgColor = if (ElementTheme.isLightTheme) {
- ElementTheme.materialColors.background
- } else {
- ElementTheme.colors.bgSubtlePrimary
- }
+ // Note: the internal shape corner radius should be 8dp, but there is a 4p value hardcoded in the internal Surface component
androidx.compose.material3.DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier
- .background(color = bgColor)
+ .background(color = ElementTheme.colors.bgCanvasDefault)
.widthIn(min = minMenuWidth),
offset = offset,
properties = properties,
content = content
)
}
+
+private val minMenuWidth = 200.dp
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt
index b8c18f99c6..f8e0fc2b78 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt
@@ -17,20 +17,26 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowRight
import androidx.compose.material.icons.filled.BugReport
-import androidx.compose.material.icons.filled.Share
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuDefaults
-import androidx.compose.material3.MenuItemColors
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
+// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1
+
@Composable
fun DropdownMenuItem(
text: @Composable () -> Unit,
@@ -39,34 +45,37 @@ fun DropdownMenuItem(
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
enabled: Boolean = true,
- colors: MenuItemColors = MenuDefaults.itemColors(),
- contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
androidx.compose.material3.DropdownMenuItem(
- text = text,
+ text = {
+ CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyLarge) {
+ text()
+ }
+ },
onClick = onClick,
modifier = modifier,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
enabled = enabled,
- colors = colors,
- contentPadding = contentPadding,
+ colors = DropDownMenuItemDefaults.colors(),
+ contentPadding = DropDownMenuItemDefaults.contentPadding,
interactionSource = interactionSource
)
}
-@Composable
-fun DropdownMenuItemText(
- text: String,
- modifier: Modifier = Modifier,
-) {
- Text(
- text = text,
- color = ElementTheme.materialColors.primary,
- style = ElementTheme.typography.fontBodyLgRegular,
- modifier = modifier,
+internal object DropDownMenuItemDefaults {
+ @Composable
+ fun colors() = MenuDefaults.itemColors(
+ textColor = ElementTheme.colors.textPrimary,
+ leadingIconColor = ElementTheme.colors.iconPrimary,
+ trailingIconColor = ElementTheme.colors.iconSecondary,
+ disabledTextColor = ElementTheme.colors.textDisabled,
+ disabledLeadingIconColor = ElementTheme.colors.iconDisabled,
+ disabledTrailingIconColor = ElementTheme.colors.iconDisabled,
)
+
+ val contentPadding = PaddingValues(all = 12.dp)
}
@Preview(group = PreviewGroup.Menus)
@@ -75,10 +84,36 @@ internal fun DropdownMenuItemPreview() = ElementThemedPreview { ContentToPreview
@Composable
private fun ContentToPreview() {
- DropdownMenuItem(
- text = { DropdownMenuItemText(text = "Item") },
- onClick = {},
- leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
- trailingIcon = { Icon(Icons.Default.Share, contentDescription = null) },
- )
+ Column {
+ DropdownMenuItem(
+ text = { Text(text = "Item") },
+ onClick = {},
+ trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ )
+ Divider()
+ DropdownMenuItem(
+ text = { Text(text = "Item") },
+ onClick = {},
+ leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
+ )
+ DropdownMenuItem(
+ text = { Text(text = "Item") },
+ onClick = {},
+ leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
+ trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ )
+ DropdownMenuItem(
+ text = { Text(text = "Item") },
+ onClick = {},
+ enabled = false,
+ leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
+ trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ )
+ Divider()
+ DropdownMenuItem(
+ text = { Text(text = "Multiline\nItem") },
+ onClick = {},
+ trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) },
+ )
+ }
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt
index 14cc6b62ce..7063ab1eb1 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt
@@ -17,16 +17,22 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+
+// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1182%3A48861&mode=design&t=Shlcvznm1oUyqGC2-1
@Composable
fun IconButton(
@@ -36,11 +42,15 @@ fun IconButton(
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable () -> Unit
) {
+ val colors = IconButtonDefaults.iconButtonColors(
+ contentColor = LocalContentColor.current,
+ disabledContentColor = ElementTheme.colors.iconDisabled,
+ )
androidx.compose.material3.IconButton(
onClick = onClick,
modifier = modifier,
enabled = enabled,
- colors = IconButtonDefaults.iconButtonColors(),
+ colors = colors,
interactionSource = interactionSource,
content = content,
)
@@ -53,12 +63,26 @@ internal fun IconButtonPreview() =
@Composable
private fun ContentToPreview() {
- Row {
- IconButton(onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ Column {
+ CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconPrimary) {
+ Row {
+ IconButton(onClick = {}) {
+ Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ }
+ IconButton(enabled = false, onClick = {}) {
+ Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ }
+ }
}
- IconButton(enabled = false, onClick = {}) {
- Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) {
+ Row {
+ IconButton(onClick = {}) {
+ Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ }
+ IconButton(enabled = false, onClick = {}) {
+ Icon(imageVector = Icons.Filled.Close, contentDescription = "")
+ }
+ }
}
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt
new file mode 100644
index 0000000000..54985eaa51
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.theme.components
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.ProgressIndicatorDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.platform.LocalInspectionMode
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementThemedPreview
+import io.element.android.libraries.designsystem.preview.PreviewGroup
+
+@Composable
+fun LinearProgressIndicator(
+ progress: Float,
+ modifier: Modifier = Modifier,
+ color: Color = ProgressIndicatorDefaults.linearColor,
+ trackColor: Color = ProgressIndicatorDefaults.linearTrackColor,
+ strokeCap: StrokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
+) {
+ androidx.compose.material3.LinearProgressIndicator(
+ modifier = modifier,
+ progress = progress,
+ color = color,
+ trackColor = trackColor,
+ strokeCap = strokeCap,
+ )
+}
+
+@Composable
+fun LinearProgressIndicator(
+ modifier: Modifier = Modifier,
+ color: Color = ProgressIndicatorDefaults.linearColor,
+ trackColor: Color = ProgressIndicatorDefaults.linearTrackColor,
+ strokeCap: StrokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
+) {
+ if (LocalInspectionMode.current) {
+ // Use a determinate progress indicator to improve the preview rendering
+ androidx.compose.material3.LinearProgressIndicator(
+ modifier = modifier,
+ progress = 0.75F,
+ color = color,
+ trackColor = trackColor,
+ strokeCap = strokeCap,
+ )
+ } else {
+ androidx.compose.material3.LinearProgressIndicator(
+ modifier = modifier,
+ color = color,
+ trackColor = trackColor,
+ strokeCap = strokeCap,
+ )
+ }
+}
+
+@Preview(group = PreviewGroup.Progress)
+@Composable
+internal fun LinearProgressIndicatorPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() }
+
+@Composable
+private fun ContentToPreview() {
+ Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
+ // Indeterminate progress
+ LinearProgressIndicator(
+ )
+ // Fixed progress
+ LinearProgressIndicator(
+ progress = 0.90F
+ )
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
index d3cd2fee07..d868f49965 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt
@@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
+import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -43,7 +49,11 @@ fun MediumTopAppBar(
title = title,
modifier = modifier,
navigationIcon = navigationIcon,
- actions = actions,
+ actions = {
+ CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) {
+ actions()
+ }
+ },
windowInsets = windowInsets,
colors = colors,
scrollBehavior = scrollBehavior,
@@ -58,5 +68,14 @@ internal fun MediumTopAppBarPreview() =
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview() {
- MediumTopAppBar(title = { Text(text = "Title") })
+ MediumTopAppBar(
+ title = { Text(text = "Title") },
+ navigationIcon = { BackButton(onClick = {}) },
+ actions = {
+ TextButton(text = "Action", onClick = {})
+ IconButton(onClick = {}) {
+ Icon(imageVector = Icons.Default.Share, contentDescription = null)
+ }
+ }
+ )
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt
index a31e4188c6..57b0612ad7 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt
@@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetState
-import androidx.compose.material3.SheetValue
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
@@ -38,6 +37,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.designsystem.preview.sheetStateForPreview
import io.element.android.libraries.theme.ElementTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -100,10 +100,7 @@ private fun ContentToPreview() {
) {
ModalBottomSheet(
onDismissRequest = {},
- sheetState = SheetState(
- skipPartiallyExpanded = true,
- initialValue = SheetValue.Expanded,
- ),
+ sheetState = sheetStateForPreview,
) {
Text(
text = "Sheet Content",
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt
deleted file mode 100644
index fa7ee261f6..0000000000
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.libraries.designsystem.theme.components
-
-import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.material3.ButtonColors
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.ButtonElevation
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.ElementThemedPreview
-import io.element.android.libraries.designsystem.preview.PreviewGroup
-
-@Composable
-fun OutlinedButton(
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- enabled: Boolean = true,
- shape: Shape = ElementOutlinedButtonDefaults.shape,
- colors: ButtonColors = ElementOutlinedButtonDefaults.buttonColors(),
- elevation: ButtonElevation? = ElementOutlinedButtonDefaults.buttonElevation(),
- border: BorderStroke? = ElementOutlinedButtonDefaults.border,
- contentPadding: PaddingValues = ElementOutlinedButtonDefaults.ContentPadding,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- content: @Composable RowScope.() -> Unit
-) {
- androidx.compose.material3.Button(
- onClick = onClick,
- modifier = modifier,
- enabled = enabled,
- shape = shape,
- colors = colors,
- elevation = elevation,
- border = border,
- contentPadding = contentPadding,
- interactionSource = interactionSource,
- content = content,
- )
-}
-
-object ElementOutlinedButtonDefaults {
- val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp)
- val shape: Shape @Composable get() = ButtonDefaults.outlinedShape
- val border: BorderStroke @Composable get() = ButtonDefaults.outlinedButtonBorder
- @Composable
- fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation()
-
- @Composable
- fun buttonColors(): ButtonColors = ButtonDefaults.outlinedButtonColors()
-
-
-}
-
-@Preview(group = PreviewGroup.Buttons)
-@Composable
-internal fun OutlinedButtonsPreview() = ElementThemedPreview { ContentToPreview() }
-
-@Composable
-private fun ContentToPreview() {
- Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
- OutlinedButton(onClick = {}, enabled = true) {
- Text(text = "Click me! - Enabled")
- }
- OutlinedButton(onClick = {}, enabled = false) {
- Text(text = "Click me! - Disabled")
- }
- }
-}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt
index 6b0c1b377e..a8b186a6b2 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt
@@ -17,15 +17,21 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.material3.RadioButtonColors
import androidx.compose.material3.RadioButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+
+// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24202&mode=design&t=qb99xBP5mwwCtGkN-1
@Composable
fun RadioButton(
@@ -33,7 +39,7 @@ fun RadioButton(
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
- colors: RadioButtonColors = RadioButtonDefaults.colors(),
+ colors: RadioButtonColors = compoundRadioButtonColors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
androidx.compose.material3.RadioButton(
@@ -46,6 +52,15 @@ fun RadioButton(
)
}
+@Composable
+internal fun compoundRadioButtonColors(): RadioButtonColors {
+ return RadioButtonDefaults.colors(
+ unselectedColor = ElementTheme.colors.borderInteractivePrimary,
+ disabledUnselectedColor = ElementTheme.colors.borderDisabled,
+ disabledSelectedColor = ElementTheme.colors.iconDisabled,
+ )
+}
+
@Preview(group = PreviewGroup.Toggles)
@Composable
internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() }
@@ -53,9 +68,13 @@ internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { Con
@Composable
private fun ContentToPreview() {
Column {
- RadioButton(selected = false, onClick = {})
- RadioButton(selected = true, onClick = {})
- RadioButton(selected = false, enabled = false, onClick = {})
- RadioButton(selected = true, enabled = false, onClick = {})
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ RadioButton(selected = false, onClick = {})
+ RadioButton(selected = false, enabled = false, onClick = {})
+ }
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ RadioButton(selected = true, onClick = {})
+ RadioButton(selected = true, enabled = false, onClick = {})
+ }
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt
new file mode 100644
index 0000000000..d2969fc3e9
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.theme.components
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.SnackbarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.components.button.ButtonVisuals
+import io.element.android.libraries.designsystem.preview.ElementThemedPreview
+import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.theme.SnackBarLabelColorDark
+import io.element.android.libraries.theme.SnackBarLabelColorLight
+
+@Composable
+fun Snackbar(
+ message: String,
+ modifier: Modifier = Modifier,
+ action: ButtonVisuals? = null,
+ dismissAction: ButtonVisuals? = null,
+ actionOnNewLine: Boolean = false,
+ shape: Shape = RoundedCornerShape(8.dp),
+ containerColor: Color = SnackbarDefaults.color,
+ contentColor: Color = ElementTheme.materialColors.inverseOnSurface,
+ actionContentColor: Color = actionContentColor(),
+ dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor,
+) {
+ Snackbar(
+ modifier = modifier,
+ action = action?.let { @Composable { it.Composable() } },
+ dismissAction = dismissAction?.let { @Composable { it.Composable() } },
+ actionOnNewLine = actionOnNewLine,
+ shape = shape,
+ containerColor = containerColor,
+ contentColor = contentColor,
+ actionContentColor = actionContentColor,
+ dismissActionContentColor = dismissActionContentColor,
+ content = { Text(text = message) },
+ )
+}
+
+@Composable
+fun Snackbar(
+ modifier: Modifier = Modifier,
+ action: @Composable (() -> Unit)? = null,
+ dismissAction: @Composable (() -> Unit)? = null,
+ actionOnNewLine: Boolean = false,
+ shape: Shape = RoundedCornerShape(8.dp),
+ containerColor: Color = SnackbarDefaults.color,
+ contentColor: Color = ElementTheme.materialColors.inverseOnSurface,
+ actionContentColor: Color = actionContentColor(),
+ dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor,
+ content: @Composable () -> Unit
+) {
+ androidx.compose.material3.Snackbar(
+ modifier = modifier,
+ action = action,
+ dismissAction = dismissAction,
+ actionOnNewLine = actionOnNewLine,
+ shape = shape,
+ containerColor = containerColor,
+ contentColor = contentColor,
+ actionContentColor = actionContentColor,
+ dismissActionContentColor = dismissActionContentColor,
+ content = content,
+ )
+}
+
+// TODO this color is temporary, an `inverse` version should be added to the semantic colors instead
+@Composable
+private fun actionContentColor(): Color {
+ return if (ElementTheme.isLightTheme) {
+ SnackBarLabelColorLight
+ } else {
+ SnackBarLabelColorDark
+ }
+}
+
+@Preview(name = "Snackbar", group = PreviewGroup.Snackbars)
+@Composable
+internal fun SnackbarPreview() {
+ ElementThemedPreview {
+ Snackbar(message = "Snackbar supporting text")
+ }
+}
+
+@Preview(name = "Snackbar with action", group = PreviewGroup.Snackbars)
+@Composable
+internal fun SnackbarWithActionPreview() {
+ ElementThemedPreview {
+ Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}))
+ }
+}
+
+@Preview(name = "Snackbar with action and close button", group = PreviewGroup.Snackbars)
+@Composable
+internal fun SnackbarWithActionAndCloseButtonPreview() {
+ ElementThemedPreview {
+ Snackbar(
+ message = "Snackbar supporting text",
+ action = ButtonVisuals.Text("Action", {}),
+ dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {})
+ )
+ }
+}
+
+@Preview(name = "Snackbar with action on new line", group = PreviewGroup.Snackbars)
+@Composable
+internal fun SnackbarWithActionOnNewLinePreview() {
+ ElementThemedPreview {
+ Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}), actionOnNewLine = true)
+ }
+}
+
+@Preview(name = "Snackbar with action and close button on new line", group = PreviewGroup.Snackbars)
+@Composable
+internal fun SnackbarWithActionOnNewLineAndCloseButtonPreview() {
+ ElementThemedPreview {
+ Snackbar(
+ message = "Snackbar supporting text",
+ action = ButtonVisuals.Text("Action", {}),
+ dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {}),
+ actionOnNewLine = true
+ )
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt
new file mode 100644
index 0000000000..ab4c9dee05
--- /dev/null
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.designsystem.theme.components
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.SwitchColors
+import androidx.compose.material3.SwitchDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.ElementThemedPreview
+import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
+import androidx.compose.material3.Switch as Material3Switch
+
+// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24203&mode=design&t=qb99xBP5mwwCtGkN-1
+
+@Composable
+fun Switch(
+ checked: Boolean,
+ onCheckedChange: ((Boolean) -> Unit)?,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ colors: SwitchColors = compoundSwitchColors(),
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ thumbContent: (@Composable () -> Unit)? = null,
+) {
+ Material3Switch(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ modifier = modifier,
+ enabled = enabled,
+ colors = colors,
+ interactionSource = interactionSource,
+ thumbContent = thumbContent
+ )
+}
+
+@Composable
+internal fun compoundSwitchColors() = SwitchDefaults.colors(
+ uncheckedThumbColor = ElementTheme.colors.bgActionPrimaryRest,
+ uncheckedTrackColor = Color.Transparent,
+ disabledUncheckedBorderColor = ElementTheme.colors.borderDisabled,
+ disabledUncheckedThumbColor = ElementTheme.colors.iconDisabled,
+ disabledCheckedTrackColor = ElementTheme.colors.iconDisabled,
+ disabledCheckedBorderColor = ElementTheme.colors.iconDisabled,
+)
+
+@Preview(group = PreviewGroup.Toggles)
+@Composable
+internal fun SwitchPreview() {
+ var checked by remember { mutableStateOf(false) }
+ ElementThemedPreview {
+ Column(modifier = Modifier.padding(10.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Switch(checked = checked, onCheckedChange = { checked = !checked })
+ Switch(enabled = false, checked = checked, onCheckedChange = { checked = !checked })
+ }
+ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
+ Switch(checked = !checked, onCheckedChange = { checked = !checked })
+ Switch(enabled = false, checked = !checked, onCheckedChange = { checked = !checked })
+ }
+ }
+ }
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt
deleted file mode 100644
index 3b4b50a5e7..0000000000
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.libraries.designsystem.theme.components
-
-import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.material3.ButtonColors
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.ButtonElevation
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.tooling.preview.Preview
-import io.element.android.libraries.designsystem.preview.ElementThemedPreview
-import io.element.android.libraries.designsystem.preview.PreviewGroup
-
-@Composable
-fun TextButton(
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- enabled: Boolean = true,
- shape: Shape = ButtonDefaults.textShape,
- colors: ButtonColors = ButtonDefaults.textButtonColors(),
- elevation: ButtonElevation? = null,
- border: BorderStroke? = null,
- contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- content: @Composable RowScope.() -> Unit
-) {
- androidx.compose.material3.TextButton(
- onClick = onClick,
- modifier = modifier,
- enabled = enabled,
- shape = shape,
- colors = colors,
- elevation = elevation,
- border = border,
- contentPadding = contentPadding,
- interactionSource = interactionSource,
- content = content,
- )
-}
-
-@Preview(group = PreviewGroup.Buttons)
-@Composable
-internal fun TextButtonPreview() = ElementThemedPreview { ContentToPreview() }
-
-@Composable
-private fun ContentToPreview() {
- Column {
- TextButton(onClick = {}, enabled = true) {
- Text(text = "Click me! - Enabled")
- }
- TextButton(onClick = {}, enabled = false) {
- Text(text = "Click me! - Disabled")
- }
- }
-}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
index 23848ef76d..93d11e8c9a 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt
@@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
+import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
+import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -43,7 +49,11 @@ fun TopAppBar(
title = title,
modifier = modifier,
navigationIcon = navigationIcon,
- actions = actions,
+ actions = {
+ CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) {
+ actions()
+ }
+ },
windowInsets = windowInsets,
colors = colors,
scrollBehavior = scrollBehavior,
@@ -58,5 +68,14 @@ internal fun TopAppBarPreview() =
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview() {
- TopAppBar(title = { Text(text = "Title") })
+ TopAppBar(
+ title = { Text(text = "Title") },
+ navigationIcon = { BackButton(onClick = {}) },
+ actions = {
+ TextButton(text = "Action", onClick = {})
+ IconButton(onClick = {}) {
+ Icon(imageVector = Icons.Default.Share, contentDescription = null)
+ }
+ }
+ )
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt
index a422c5e729..45b5eb39be 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt
@@ -23,7 +23,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
-import io.element.android.libraries.designsystem.components.dialogs.AlertDialogContent
+import io.element.android.libraries.designsystem.theme.components.AlertDialogContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewGroup
@@ -44,7 +44,7 @@ internal fun DatePickerPreviewDark() {
@Composable
private fun ContentToPreview() {
val state = rememberDatePickerState(
- initialSelectedDateMillis = 1672578000000L,
+ initialSelectedDateMillis = 1_672_578_000_000L,
)
AlertDialogContent(
buttons = { /*TODO*/ },
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt
index f1c2cd444d..2ca04f1008 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt
@@ -30,7 +30,6 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
-import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
@@ -39,9 +38,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
internal fun MenuPreview() {
ElementThemedPreview {
var isExpanded by remember { mutableStateOf(false) }
- Button(onClick = { isExpanded = !isExpanded }) {
- Text("Toggle")
- }
+ Button(text = "Toggle", onClick = { isExpanded = !isExpanded })
DropdownMenu(expanded = isExpanded, onDismissRequest = { isExpanded = false }) {
for (i in 0..5) {
val leadingIcon: @Composable (() -> Unit)? = if (i in 2..3) {
@@ -60,7 +57,7 @@ internal fun MenuPreview() {
null
}
DropdownMenuItem(
- text = { DropdownMenuItemText(text = "Item $i") },
+ text = { Text(text = "Item $i") },
onClick = { isExpanded = false },
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt
deleted file mode 100644
index 11491a0a1c..0000000000
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.libraries.designsystem.theme.components.previews
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Check
-import androidx.compose.material3.Switch
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import io.element.android.libraries.designsystem.preview.ElementThemedPreview
-import io.element.android.libraries.designsystem.preview.PreviewGroup
-import io.element.android.libraries.designsystem.theme.components.Icon
-
-@Preview(group = PreviewGroup.Toggles)
-@Composable
-internal fun SwitchPreview() {
- ElementThemedPreview {
- Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- var checked by remember { mutableStateOf(false) }
- Switch(checked = checked, onCheckedChange = { checked = !checked })
- Switch(checked = checked, onCheckedChange = { checked = !checked }, thumbContent = {
- Icon(imageVector = Icons.Outlined.Check, contentDescription = null)
- })
- Switch(checked = checked, enabled = false, onCheckedChange = { checked = !checked })
- Switch(checked = checked, enabled = false, onCheckedChange = { checked = !checked }, thumbContent = {
- Icon(imageVector = Icons.Outlined.Check, contentDescription = null)
- })
- }
- }
-}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt
index 79f0fffbee..7aae42ed0e 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt
@@ -24,7 +24,7 @@ import androidx.compose.material3.TimePickerLayoutType
import androidx.compose.material3.rememberTimePickerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
-import io.element.android.libraries.designsystem.components.dialogs.AlertDialogContent
+import io.element.android.libraries.designsystem.theme.components.AlertDialogContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt
index f6edd1a8fb..2618a5de82 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt
@@ -28,10 +28,12 @@ import timber.log.Timber
@Composable
fun LogCompositions(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
- val ref = remember { Ref(0) }
+ val ref = remember { Ref() }
SideEffect { ref.value++ }
- Timber.d(tag, "Compositions: $msg ${ref.value}")
+ Timber.tag(tag).d("Compositions: $msg ${ref.value}")
}
}
-class Ref(var value: Int)
+private class Ref {
+ var value: Int = 0
+}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt
index f4e44779d1..4513a90914 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt
@@ -17,6 +17,8 @@
package io.element.android.libraries.designsystem.utils
import androidx.annotation.StringRes
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
@@ -25,7 +27,11 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import io.element.android.libraries.designsystem.components.button.ButtonVisuals
+import io.element.android.libraries.designsystem.theme.components.IconSource
+import io.element.android.libraries.designsystem.theme.components.Snackbar
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -65,6 +71,19 @@ fun SnackbarDispatcher.collectSnackbarMessageAsState(): State
return snackbarMessage.collectAsState(initial = null)
}
+@Composable
+fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
+ androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
+ Snackbar(
+ message = data.visuals.message,
+ action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
+ dismissAction = if (data.visuals.withDismissAction) {
+ ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), data::dismiss)
+ } else null,
+ )
+ }
+}
+
@Composable
fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostState {
val snackbarHostState = remember { SnackbarHostState() }
diff --git a/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml b/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml
deleted file mode 100644
index 96a220a5cd..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
diff --git a/libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml b/libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml
deleted file mode 100644
index 739053947d..0000000000
--- a/libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
index f1af61c8e7..0736cf61ce 100644
--- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
+++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt
@@ -37,6 +37,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
@@ -94,6 +96,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
is StateContent -> {
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
}
+ is PollContent, is PollEndContent, // TODO Polls: handle last message
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom)
}
diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
index 8f89233a31..42d8aae083 100644
--- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
+++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt
@@ -26,6 +26,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventTimeline
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
@@ -63,6 +65,8 @@ class DefaultTimelineEventFormatter @Inject constructor(
}
RedactedContent,
is StickerContent,
+ is PollContent,
+ is PollEndContent,
is UnableToDecryptContent,
is MessageContent,
is FailedToParseMessageLikeContent,
diff --git a/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml b/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..4c1defbb68
--- /dev/null
+++ b/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,57 @@
+
+
+ "(аватар тоже был изменен)"
+ "%1$s сменили свой аватар"
+ "Вы сменили аватар"
+ "%1$s изменил свое отображаемое имя с %2$s на %3$s"
+ "Вы изменили свое отображаемое имя с %1$s на %2$s"
+ "%1$s удалил свое отображаемое имя (оно было %2$s)"
+ "Вы удалили свое отображаемое имя (оно было %1$s)"
+ "%1$s установили свое отображаемое имя на %2$s"
+ "Вы установили отображаемое имя на %1$s"
+ "%1$s изменил аватар комнаты"
+ "Вы изменили аватар комнаты"
+ "%1$s удалил аватар комнаты"
+ "Вы удалили аватар комнаты"
+ "%1$s заблокирован %2$s"
+ "Вы заблокировали %1$s"
+ "%1$s создал комнату"
+ "Вы создали комнату"
+ "%1$s пригласил %2$s"
+ "%1$s принял приглашение"
+ "Вы приняли приглашение"
+ "Вы пригласили %1$s"
+ "Пользователь %1$s пригласил вас"
+ "%1$s присоединился к комнате"
+ "Вы вошли в комнату"
+ "%1$s запросил присоединение"
+ "%1$s разрешил %2$s присоединиться"
+ "%1$s разрешил вам присоединиться"
+ "Вы запросили присоединение"
+ "%1$s отклонил запрос %2$s на присоединение"
+ "Вы отклонили запрос %1$s на присоединение"
+ "%1$s отклонил ваш запрос на присоединение"
+ "%1$s больше не заинтересован в присоединении"
+ "Вы отменили запрос на присоединение"
+ "%1$s покинул комнату"
+ "Вы вышли из комнаты"
+ "%1$s изменил название комнаты на: %2$s"
+ "Вы изменили название комнаты на: %1$s"
+ "%1$s удалил название комнаты"
+ "Вы удалили название комнаты"
+ "%1$s отклонил приглашение"
+ "Вы отклонили приглашение"
+ "%1$s удалил %2$s"
+ "Вы удалили %1$s"
+ "%1$s отправила приглашение %2$s присоединиться к комнате"
+ "Вы отправили приглашение присоединиться к комнате %1$s"
+ "%1$s отозвал приглашение %2$s присоединиться к комнате"
+ "Вы отозвали приглашение %1$s присоединиться к комнате"
+ "%1$s изменил тему на: %2$s"
+ "Вы изменили тему на: %1$s"
+ "%1$s удалил тему комнаты"
+ "Вы удалили тему комнаты"
+ "%1$s разблокирован %2$s"
+ "Вы разблокировали %1$s"
+ "%1$s внес неизвестное изменение в составе"
+
diff --git a/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..45ab0acee1
--- /dev/null
+++ b/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,37 @@
+
+
+ "%1$s將他的顯示名稱從%2$s變更為%3$s"
+ "您將您的顯示名稱從%1$s1變更為%2$s"
+ "%1$s的顯示名稱已被本人移除(原為%2$s)"
+ "您的顯示名稱已被您移除(原為%1$s)"
+ "%1$s將他的顯示名稱設為%2$s"
+ "您將您的顯示名稱設為%1$s"
+ "%1$s建立此聊天室"
+ "您建立此聊天室"
+ "%1$s邀請%2$s"
+ "%1$s接受邀請"
+ "您接受邀請"
+ "您邀請%1$s"
+ "%1$s邀請您"
+ "%1$s加入聊天室"
+ "您加入聊天室"
+ "%1$s請求加入"
+ "您請求加入"
+ "%1$s拒絕%2$s的加入請求"
+ "您拒絕%1$s的加入請求"
+ "%1$s拒絕您的加入請求"
+ "%1$s離開聊天室"
+ "您離開聊天室"
+ "%1$s將聊天室名稱變更為%2$s"
+ "您將聊天室名稱變更為%1$s"
+ "聊天室名稱已被%1$s移除"
+ "聊天室名稱已被您移除"
+ "%2$s已被%1$s移除"
+ "%1$s已被您移除"
+ "%1$s邀請%2$s加入聊天室"
+ "您邀請%1$s加入聊天室"
+ "%1$s將主題變更為%2$s"
+ "您將主題變更為%1$s"
+ "聊天室主題已被%1$s移除"
+ "聊天室主題已被您移除"
+
diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
index 920be09389..4c1bfed33a 100644
--- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
+++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
@@ -22,16 +22,8 @@ enum class FeatureFlags(
override val description: String? = null,
override val defaultValue: Boolean = true
) : Feature {
- CollapseRoomStateEvents(
- key = "feature.collapseroomstateevents",
- title = "Collapse room state events",
- ),
- ShowStartChatFlow(
- key = "feature.showstartchatflow",
- title = "Show start chat flow",
- ),
- ShowMediaUploadingFlow(
- key = "feature.showmediauploadingflow",
- title = "Show media uploading flow",
+ LocationSharing(
+ key = "feature.locationsharing",
+ title = "Allow user to share location",
)
}
diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt
index ae498e67df..d226885495 100644
--- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt
+++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt
@@ -29,9 +29,7 @@ class BuildtimeFeatureFlagProvider @Inject constructor() :
override suspend fun isFeatureEnabled(feature: Feature): Boolean {
return if (feature is FeatureFlags) {
when (feature) {
- FeatureFlags.CollapseRoomStateEvents -> false
- FeatureFlags.ShowStartChatFlow -> false
- FeatureFlags.ShowMediaUploadingFlow -> false
+ FeatureFlags.LocationSharing -> true
}
} else {
false
diff --git a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt
index 5f7f01423a..ea9b03acdf 100644
--- a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt
+++ b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt
@@ -26,14 +26,14 @@ class DefaultFeatureFlagServiceTest {
@Test
fun `given service without provider when feature is checked then it returns the default value`() = runTest {
val featureFlagService = DefaultFeatureFlagService(emptySet())
- val isFeatureEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)
- assertThat(isFeatureEnabled).isEqualTo(FeatureFlags.ShowStartChatFlow.defaultValue)
+ val isFeatureEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)
+ assertThat(isFeatureEnabled).isEqualTo(FeatureFlags.LocationSharing.defaultValue)
}
@Test
fun `given service without provider when set enabled feature is called then it returns false`() = runTest {
val featureFlagService = DefaultFeatureFlagService(emptySet())
- val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true)
+ val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
assertThat(result).isEqualTo(false)
}
@@ -41,7 +41,7 @@ class DefaultFeatureFlagServiceTest {
fun `given service with a runtime provider when set enabled feature is called then it returns true`() = runTest {
val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0)
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider))
- val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true)
+ val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
assertThat(result).isEqualTo(true)
}
@@ -49,10 +49,10 @@ class DefaultFeatureFlagServiceTest {
fun `given service with a runtime provider and feature enabled when feature is checked then it returns the correct value`() = runTest {
val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0)
val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider))
- featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true)
- assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)).isEqualTo(true)
- featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, false)
- assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)).isEqualTo(false)
+ featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true)
+ assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true)
+ featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, false)
+ assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(false)
}
@Test
@@ -60,8 +60,8 @@ class DefaultFeatureFlagServiceTest {
val lowPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(LOW_PRIORITY)
val highPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(HIGH_PRIORITY)
val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityfeatureFlagProvider, highPriorityfeatureFlagProvider))
- lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, false)
- highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true)
- assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)).isEqualTo(true)
+ lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, false)
+ highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, true)
+ assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true)
}
}
diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt
index 4b7b7005f2..69e0e9b1d4 100644
--- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt
+++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt
@@ -18,6 +18,8 @@
package io.element.android.libraries.maplibre.compose
+import androidx.compose.ui.graphics.Color
+
internal val DefaultMapLocationSettings = MapLocationSettings()
/**
@@ -28,4 +30,11 @@ internal val DefaultMapLocationSettings = MapLocationSettings()
*/
public data class MapLocationSettings(
public val locationEnabled: Boolean = false,
+ public val backgroundTintColor: Color = Color.Unspecified,
+ public val foregroundTintColor: Color = Color.Unspecified,
+ public val backgroundStaleTintColor: Color = Color.Unspecified,
+ public val foregroundStaleTintColor: Color = Color.Unspecified,
+ public val accuracyColor: Color = Color.Unspecified,
+ public val pulseEnabled: Boolean = false,
+ public val pulseColor: Color = Color.Unspecified
)
diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt
index d7d5f9ca11..e4e3565f22 100644
--- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt
+++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt
@@ -39,6 +39,7 @@ internal class MapPropertiesNode(
style: Style,
context: Context,
cameraPositionState: CameraPositionState,
+ locationSettings: MapLocationSettings,
) : MapNode {
init {
@@ -46,7 +47,13 @@ internal class MapPropertiesNode(
LocationComponentActivationOptions.Builder(context, style)
.locationComponentOptions(
LocationComponentOptions.builder(context)
- .pulseEnabled(true)
+ .backgroundTintColor(locationSettings.backgroundTintColor.toArgb())
+ .foregroundTintColor(locationSettings.foregroundTintColor.toArgb())
+ .backgroundStaleTintColor(locationSettings.backgroundStaleTintColor.toArgb())
+ .foregroundStaleTintColor(locationSettings.foregroundStaleTintColor.toArgb())
+ .accuracyColor(locationSettings.accuracyColor.toArgb())
+ .pulseEnabled(locationSettings.pulseEnabled)
+ .pulseColor(locationSettings.pulseColor.toArgb())
.build()
)
.locationEngineRequest(
@@ -116,9 +123,9 @@ internal class MapPropertiesNode(
@Composable
internal inline fun MapUpdater(
cameraPositionState: CameraPositionState,
- mapLocationSettings: MapLocationSettings,
- mapUiSettings: MapUiSettings,
- mapSymbolManagerSettings: MapSymbolManagerSettings,
+ locationSettings: MapLocationSettings,
+ uiSettings: MapUiSettings,
+ symbolManagerSettings: MapSymbolManagerSettings,
) {
val mapApplier = currentComposer.applier as MapApplier
val map = mapApplier.map
@@ -132,21 +139,22 @@ internal inline fun MapUpdater(
style = style,
context = context,
cameraPositionState = cameraPositionState,
+ locationSettings = locationSettings,
)
},
update = {
- set(mapLocationSettings.locationEnabled) { map.locationComponent.isLocationComponentEnabled = it }
+ set(locationSettings.locationEnabled) { map.locationComponent.isLocationComponentEnabled = it }
- set(mapUiSettings.compassEnabled) { map.uiSettings.isCompassEnabled = it }
- set(mapUiSettings.rotationGesturesEnabled) { map.uiSettings.isRotateGesturesEnabled = it }
- set(mapUiSettings.scrollGesturesEnabled) { map.uiSettings.isScrollGesturesEnabled = it }
- set(mapUiSettings.tiltGesturesEnabled) { map.uiSettings.isTiltGesturesEnabled = it }
- set(mapUiSettings.zoomGesturesEnabled) { map.uiSettings.isZoomGesturesEnabled = it }
- set(mapUiSettings.logoGravity) { map.uiSettings.logoGravity = it }
- set(mapUiSettings.attributionGravity) { map.uiSettings.attributionGravity = it }
- set(mapUiSettings.attributionTintColor) { map.uiSettings.setAttributionTintColor(it.toArgb()) }
+ set(uiSettings.compassEnabled) { map.uiSettings.isCompassEnabled = it }
+ set(uiSettings.rotationGesturesEnabled) { map.uiSettings.isRotateGesturesEnabled = it }
+ set(uiSettings.scrollGesturesEnabled) { map.uiSettings.isScrollGesturesEnabled = it }
+ set(uiSettings.tiltGesturesEnabled) { map.uiSettings.isTiltGesturesEnabled = it }
+ set(uiSettings.zoomGesturesEnabled) { map.uiSettings.isZoomGesturesEnabled = it }
+ set(uiSettings.logoGravity) { map.uiSettings.logoGravity = it }
+ set(uiSettings.attributionGravity) { map.uiSettings.attributionGravity = it }
+ set(uiSettings.attributionTintColor) { map.uiSettings.setAttributionTintColor(it.toArgb()) }
- set(mapSymbolManagerSettings.iconAllowOverlap) { symbolManager.iconAllowOverlap = it }
+ set(symbolManagerSettings.iconAllowOverlap) { symbolManager.iconAllowOverlap = it }
update(cameraPositionState) { this.cameraPositionState = it }
}
diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt
index 3c3cf3e44f..5af79e7524 100644
--- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt
+++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt
@@ -124,9 +124,9 @@ public fun MapboxMap(
) {
MapUpdater(
cameraPositionState = currentCameraPositionState,
- mapUiSettings = currentUiSettings,
- mapLocationSettings = currentMapLocationSettings,
- mapSymbolManagerSettings = currentSymbolManagerSettings,
+ uiSettings = currentUiSettings,
+ locationSettings = currentMapLocationSettings,
+ symbolManagerSettings = currentSymbolManagerSettings,
)
CompositionLocalProvider(
LocalCameraPositionState provides cameraPositionState,
@@ -236,7 +236,7 @@ private fun MapView.lifecycleObserver(previousState: MutableState {
//handled in onDispose
}
- else -> throw IllegalStateException()
+ Lifecycle.Event.ON_ANY -> error("ON_ANY should never be used")
}
previousState.value = event
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt
index 747de5f554..67c0625a91 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt
@@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
import io.element.android.libraries.matrix.api.user.MatrixUser
@@ -35,7 +35,7 @@ import java.io.Closeable
interface MatrixClient : Closeable {
val sessionId: SessionId
- val roomSummaryDataSource: RoomSummaryDataSource
+ val roomListService: RoomListService
val mediaLoader: MatrixMediaLoader
suspend fun getRoom(roomId: RoomId): MatrixRoom?
suspend fun findDM(userId: UserId): MatrixRoom?
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt
index e670e02f11..48712b7ddf 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt
@@ -22,5 +22,6 @@ sealed class AuthenticationException(message: String) : Exception(message) {
class SlidingSyncNotAvailable(message: String) : AuthenticationException(message)
class SessionMissing(message: String) : AuthenticationException(message)
class Generic(message: String) : AuthenticationException(message)
- class OidcError(type: String, message: String) : AuthenticationException(message)
+ // TODO Oidc
+ // class OidcError(type: String, message: String) : AuthenticationException(message)
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt
index bc0f0c04bc..a668448752 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt
@@ -30,7 +30,8 @@ object MatrixPatterns {
// regex pattern to find matrix user ids in a string.
// See https://matrix.org/docs/spec/appendices#historical-user-ids
- private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
+ // Sadly, we need to relax the regex pattern a bit as there already exist some ids that don't match the spec.
+ private const val MATRIX_USER_IDENTIFIER_REGEX = "^@.*?$DOMAIN_REGEX$"
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
// regex pattern to find room ids in a string.
@@ -42,7 +43,8 @@ object MatrixPatterns {
private val PATTERN_CONTAIN_MATRIX_ALIAS = MATRIX_ROOM_ALIAS_REGEX.toRegex(RegexOption.IGNORE_CASE)
// regex pattern to find message ids in a string.
- private const val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
+ // Sadly, we need to relax the regex pattern a bit as there already exist some ids that don't match the spec.
+ private const val MATRIX_EVENT_IDENTIFIER_REGEX = "^\\$.+$DOMAIN_REGEX$"
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = MATRIX_EVENT_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
// regex pattern to find message ids in a string.
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
index 4ded947d56..639509a15a 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
@@ -23,7 +23,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
data class NotificationData(
- val senderId: UserId,
val eventId: EventId,
val roomId: RoomId,
val senderAvatarUrl: String?,
@@ -33,14 +32,10 @@ data class NotificationData(
val isDirect: Boolean,
val isEncrypted: Boolean,
val isNoisy: Boolean,
- val event: NotificationEvent,
-)
-
-data class NotificationEvent(
val timestamp: Long,
val content: NotificationContent,
// For images for instance
- val contentUrl: String?
+ val contentUrl: String?,
)
sealed interface NotificationContent {
@@ -61,6 +56,7 @@ sealed interface NotificationContent {
) : MessageLike
object RoomEncrypted : MessageLike
data class RoomMessage(
+ val senderId: UserId,
val messageType: MessageType
) : MessageLike
object RoomRedaction : MessageLike
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt
index 2046252930..972873ab38 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt
@@ -21,5 +21,5 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
interface NotificationService {
- fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result
+ suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt
index 6a15bfb514..31e28a40db 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt
@@ -25,7 +25,6 @@ object PermalinkBuilder {
private const val ROOM_PATH = "room/"
private const val USER_PATH = "user/"
- private const val GROUP_PATH = "group/"
private val permalinkBaseUrl get() = (MatrixConfiguration.clientPermalinkBaseUrl ?: MatrixConfiguration.matrixToPermalinkBaseUrl).also {
var baseUrl = it
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt
index fc900e4bbb..ba9cdc1e80 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt
@@ -95,9 +95,9 @@ object PermalinkParser {
return if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) {
try {
val signValidUri = Uri.parse(signUrl)
- val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException()
- val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException()
- val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException()
+ val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException("missing `authority`")
+ val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException("missing `token`")
+ val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException("missing `private_key`")
PermalinkData.RoomEmailInviteLink(
roomId = identifier,
email = email!!,
@@ -137,7 +137,8 @@ object PermalinkParser {
.parameterList
.filter {
it.mParameter == "via"
- }.map {
+ }
+ .map {
URLDecoder.decode(it.mValue, "UTF-8")
}
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt
new file mode 100644
index 0000000000..2d4abaafb5
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.api.poll
+
+data class PollAnswer(
+ val id: String,
+ val text: String
+)
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt
new file mode 100644
index 0000000000..85bb7c0256
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+package io.element.android.libraries.matrix.api.poll
+
+enum class PollKind {
+ /** Voters should see results as soon as they have voted. */
+ Disclosed,
+
+ /** Results should be only revealed when the poll is ended. */
+ Undisclosed
+}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
index be0ff447b3..f8b9e6c2c3 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
@@ -63,7 +63,11 @@ interface MatrixRoom : Closeable {
val timeline: MatrixTimeline
- fun open(): Result
+ fun destroy()
+
+ fun subscribeToSync()
+
+ fun unsubscribeFromSync()
suspend fun userDisplayName(userId: UserId): Result
@@ -101,6 +105,8 @@ interface MatrixRoom : Closeable {
suspend fun canUserInvite(userId: UserId): Result
+ suspend fun canUserRedact(userId: UserId): Result
+
suspend fun canUserSendState(userId: UserId, type: StateEventType): Result
suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result
@@ -133,6 +139,8 @@ interface MatrixRoom : Closeable {
zoomLevel: Int? = null,
assetType: AssetType? = null,
): Result
+
+ override fun close() = destroy()
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt
index 852401bffc..e0ba452efe 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt
@@ -34,3 +34,9 @@ suspend fun MatrixRoom.canSendState(type: StateEventType): Result = can
* Shortcut for calling [MatrixRoom.canUserSendMessage] with our own user.
*/
suspend fun MatrixRoom.canSendMessage(type: MessageEventType): Result = canUserSendMessage(sessionId, type)
+
+/**
+ * Shortcut for calling [MatrixRoom.canUserRedact] with our own user.
+ */
+suspend fun MatrixRoom.canRedact(): Result = canUserRedact(sessionId)
+
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt
similarity index 65%
rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt
rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt
index d677d56ed9..8714bc2c5c 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.api.room
+package io.element.android.libraries.matrix.api.roomlist
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.flow.StateFlow
@@ -23,25 +23,34 @@ import kotlinx.coroutines.withTimeout
import timber.log.Timber
import kotlin.time.Duration
-interface RoomSummaryDataSource {
-
+/**
+ * Holds some flows related to a specific set of rooms.
+ * Can be retrieved from [RoomListService] methods.
+ */
+interface RoomList {
sealed class LoadingState {
object NotLoaded : LoadingState()
data class Loaded(val numberOfRooms: Int) : LoadingState()
}
- fun updateAllRoomsVisibleRange(range: IntRange)
- fun allRoomsLoadingState(): StateFlow
- fun allRooms(): StateFlow>
- fun inviteRooms(): StateFlow>
+ /**
+ * The list of room summaries as a flow.
+ */
+ val summaries: StateFlow>
+
+ /**
+ * The loading state of the room list as a flow.
+ * This is useful to know if a specific set of rooms is loaded or not.
+ */
+ val loadingState: StateFlow
}
-suspend fun RoomSummaryDataSource.awaitAllRoomsAreLoaded(timeout: Duration = Duration.INFINITE) {
+suspend fun RoomList.awaitLoaded(timeout: Duration = Duration.INFINITE) {
try {
Timber.d("awaitAllRoomsAreLoaded: wait")
withTimeout(timeout) {
- allRoomsLoadingState().firstOrNull {
- it is RoomSummaryDataSource.LoadingState.Loaded
+ loadingState.firstOrNull {
+ it is RoomList.LoadingState.Loaded
}
}
} catch (timeoutException: TimeoutCancellationException) {
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt
new file mode 100644
index 0000000000..99381d0e74
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.api.roomlist
+
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Entry point for the room list api.
+ * This service will provide different sets of rooms (all, invites, etc.).
+ * It requires the SyncService to be started to receive updates.
+ */
+interface RoomListService {
+
+ sealed class State {
+ object Idle : State()
+ object Running : State()
+ object Error : State()
+ object Terminated : State()
+ }
+
+ /**
+ * returns a [RoomList] object of all rooms we want to display.
+ * This will exclude some rooms like the invites, or spaces.
+ */
+ fun allRooms(): RoomList
+
+ /**
+ * returns a [RoomList] object of all invites.
+ */
+ fun invites(): RoomList
+
+ /**
+ * Will set the visible range of all rooms.
+ * This is useful to load more data when the user scrolls down.
+ */
+ fun updateAllRoomsVisibleRange(range: IntRange)
+
+ /**
+ * The state of the service as a flow.
+ */
+ val state: StateFlow
+}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt
similarity index 92%
rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt
rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt
index 7dedd86b63..87cf2139d6 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.api.room
+package io.element.android.libraries.matrix.api.roomlist
import io.element.android.libraries.matrix.api.core.RoomId
+import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.message.RoomMessage
sealed interface RoomSummary {
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt
index 5271ec9bc0..994b35edc4 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt
@@ -27,7 +27,7 @@ interface SyncService {
/**
* Tries to stop the sync. If service is not syncing it has no effect.
*/
- fun stopSync(): Result
+ suspend fun stopSync(): Result
/**
* Flow of [SyncState]. Will be updated as soon as the current [SyncState] changes.
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
index 203fb30794..82c322668c 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
@@ -23,6 +23,8 @@ import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.media.VideoInfo
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+import io.element.android.libraries.matrix.api.poll.PollKind
sealed interface EventContent
@@ -44,7 +46,7 @@ sealed interface InReplyTo {
/** The event details are available. */
data class Ready(
val eventId: EventId,
- val content: MessageContent,
+ val content: EventContent,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatarUrl: String?,
@@ -69,6 +71,19 @@ data class StickerContent(
val url: String
) : EventContent
+data class PollContent(
+ val question: String,
+ val kind: PollKind,
+ val maxSelections: ULong,
+ val answers: List,
+ val votes: Map>,
+ val endTime: ULong?
+) : EventContent
+
+data class PollEndContent(
+ val startEventId: String
+) : EventContent
+
data class UnableToDecryptContent(
val data: Data
) : EventContent {
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt
index 8bea4b5330..a2e68d17d2 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt
@@ -16,10 +16,7 @@
package io.element.android.libraries.matrix.api.timeline.item.event
-import io.element.android.libraries.matrix.api.core.UserId
-
data class EventReaction(
val key: String,
- val count: Long,
- val senderIds: List
+ val senders: List
)
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt
new file mode 100644
index 0000000000..60398cffd5
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.api.timeline.item.event
+
+import io.element.android.libraries.matrix.api.core.UserId
+
+/**
+ * The sender of a reaction.
+ *
+ * @property senderId the ID of the user who sent the reaction
+ * @property timestamp the timestamp the reaction was received on the origin homeserver
+ */
+data class ReactionSender(
+ val senderId: UserId,
+ val timestamp: Long
+)
+
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt
index 8bcd602b9f..6381cc7ed8 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright (c) 2023 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.
@@ -17,64 +17,7 @@
package io.element.android.libraries.matrix.api.tracing
data class TracingConfiguration(
- val overrides: Map = emptyMap()
-) {
-
- // Order should matters
- private val targets: MutableMap = mutableMapOf(
- Target.Common to LogLevel.Warn,
- Target.Hyper to LogLevel.Warn,
- Target.Sled to LogLevel.Warn,
- Target.MatrixSdk.Root to LogLevel.Warn,
- Target.MatrixSdk.Sled to LogLevel.Warn,
- Target.MatrixSdk.Crypto to LogLevel.Debug,
- Target.MatrixSdk.HttpClient to LogLevel.Debug,
- Target.MatrixSdk.SlidingSync to LogLevel.Trace,
- Target.MatrixSdk.BaseSlidingSync to LogLevel.Trace,
- )
-
- val filter: String
- get() {
- overrides.forEach { (target, logLevel) ->
- targets[target] = logLevel
- }
- return targets.map {
- if (it.key.filter.isEmpty()) {
- it.value.filter
- } else {
- "${it.key.filter}=${it.value.filter}"
- }
- }.joinToString(separator = ",")
- }
-}
-
-sealed class Target(open val filter: String) {
- object Common : Target("")
- object Hyper : Target("hyper")
- object Sled : Target("sled")
- sealed class MatrixSdk(override val filter: String) : Target(filter) {
- object Root : MatrixSdk("matrix_sdk")
- object Sled : MatrixSdk("matrix_sdk_sled")
- object Crypto: MatrixSdk("matrix_sdk_crypto")
- object FFI : MatrixSdk("matrix_sdk_ffi")
- object HttpClient : MatrixSdk("matrix_sdk::http_client")
- object UniffiAPI : MatrixSdk("matrix_sdk_ffi::uniffi_api")
- object SlidingSync : MatrixSdk("matrix_sdk::sliding_sync")
- object BaseSlidingSync : MatrixSdk("matrix_sdk_base::sliding_sync")
- }
-}
-
-sealed class LogLevel(val filter: String) {
- object Warn : LogLevel("warn")
- object Trace : LogLevel("trace")
- object Info : LogLevel("info")
- object Debug : LogLevel("debug")
- object Error : LogLevel("error")
-}
-
-object TracingConfigurations {
- val release = TracingConfiguration(overrides = mapOf(Target.Common to LogLevel.Info))
- val debug = TracingConfiguration(overrides = mapOf(Target.Common to LogLevel.Info))
-
- fun custom(overrides: Map) = TracingConfiguration(overrides)
-}
+ val filterConfiguration: TracingFilterConfiguration,
+ val writesToLogcat: Boolean,
+ val writesToFilesConfiguration: WriteToFilesConfiguration,
+)
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt
new file mode 100644
index 0000000000..21c6954c2a
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2022 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.
+ */
+
+package io.element.android.libraries.matrix.api.tracing
+
+data class TracingFilterConfiguration(
+ val overrides: Map = emptyMap(),
+) {
+
+ // Order should matters
+ private val targetsToLogLevel: MutableMap = mutableMapOf(
+ Target.COMMON to LogLevel.Info,
+ Target.HYPER to LogLevel.Warn,
+ Target.MATRIX_SDK_CRYPTO to LogLevel.Debug,
+ Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.Debug,
+ Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.Trace,
+ Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.Trace,
+ Target.MATRIX_SDK_UI_TIMELINE to LogLevel.Info,
+ )
+
+ val filter: String
+ get() {
+ overrides.forEach { (target, logLevel) ->
+ targetsToLogLevel[target] = logLevel
+ }
+ return targetsToLogLevel.map {
+ if (it.key.filter.isEmpty()) {
+ it.value.filter
+ } else {
+ "${it.key.filter}=${it.value.filter}"
+ }
+ }.joinToString(separator = ",")
+ }
+}
+
+enum class Target(open val filter: String) {
+ COMMON(""),
+ ELEMENT("elementx"),
+ HYPER("hyper"),
+ MATRIX_SDK_FFI("matrix_sdk_ffi"),
+ MATRIX_SDK_UNIFFI_API("matrix_sdk_ffi::uniffi_api"),
+ MATRIX_SDK_CRYPTO("matrix_sdk_crypto"),
+ MATRIX_SDK_HTTP_CLIENT("matrix_sdk::http_client"),
+ MATRIX_SDK_SLIDING_SYNC("matrix_sdk::sliding_sync"),
+ MATRIX_SDK_BASE_SLIDING_SYNC("matrix_sdk_base::sliding_sync"),
+ MATRIX_SDK_UI_TIMELINE("matrix_sdk_ui::timeline"),
+}
+
+sealed class LogLevel(val filter: String) {
+ object Warn : LogLevel("warn")
+ object Trace : LogLevel("trace")
+ object Info : LogLevel("info")
+ object Debug : LogLevel("debug")
+ object Error : LogLevel("error")
+}
+
+object TracingFilterConfigurations {
+ val release = TracingFilterConfiguration(
+ overrides = mapOf(
+ Target.COMMON to LogLevel.Info,
+ Target.ELEMENT to LogLevel.Debug
+ ),
+ )
+ val debug = TracingFilterConfiguration(
+ overrides = mapOf(
+ Target.COMMON to LogLevel.Info,
+ Target.ELEMENT to LogLevel.Trace
+ )
+ )
+
+ /**
+ * Use this method to create a custom configuration where all targets will have the same log level.
+ */
+ fun custom(logLevel: LogLevel) = TracingFilterConfiguration(overrides = Target.values().associateWith { logLevel })
+
+ /**
+ * Use this method to override the log level of specific targets.
+ */
+ fun custom(overrides: Map) = TracingFilterConfiguration(overrides)
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt
similarity index 60%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt
rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt
index f32b18c9f2..4a74f83b20 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright (c) 2023 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.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.impl.tracing
+package io.element.android.libraries.matrix.api.tracing
-import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
import timber.log.Timber
-fun setupTracing(tracingConfiguration: TracingConfiguration) {
- val filter = tracingConfiguration.filter
- Timber.v("Tracing config filter = $filter")
- org.matrix.rustcomponents.sdk.setupTracing(filter)
+interface TracingService {
+ fun setupTracing(tracingConfiguration: TracingConfiguration)
+ fun createTimberTree(): Timber.Tree
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt
new file mode 100644
index 0000000000..cafa375a6a
--- /dev/null
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.api.tracing
+
+sealed class WriteToFilesConfiguration {
+ object Disabled : WriteToFilesConfiguration()
+ data class Enabled(val directory: String, val filenamePrefix: String) : WriteToFilesConfiguration()
+}
diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts
index 7786a3ee3f..016833def6 100644
--- a/libraries/matrix/impl/build.gradle.kts
+++ b/libraries/matrix/impl/build.gradle.kts
@@ -29,7 +29,7 @@ anvil {
}
dependencies {
- // api(projects.libraries.rustsdk)
+ // implementation(projects.libraries.rustsdk)
implementation(libs.matrix.sdk)
implementation(projects.libraries.di)
implementation(projects.libraries.androidutils)
@@ -45,4 +45,5 @@ dependencies {
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)
testImplementation(projects.libraries.matrix.test)
+ testImplementation(libs.coroutines.test)
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
index 640e0772a9..8f5cfa496b 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
@@ -32,8 +32,8 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
-import io.element.android.libraries.matrix.api.room.awaitAllRoomsAreLoaded
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
+import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
@@ -45,9 +45,8 @@ import io.element.android.libraries.matrix.impl.notification.RustNotificationSer
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
-import io.element.android.libraries.matrix.impl.room.RustRoomSummaryDataSource
-import io.element.android.libraries.matrix.impl.room.roomOrNull
-import io.element.android.libraries.matrix.impl.room.stateFlow
+import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
+import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
@@ -90,20 +89,20 @@ class RustMatrixClient constructor(
) : MatrixClient {
override val sessionId: UserId = UserId(client.userId())
- private val roomListService = syncService.roomListService()
+ private val innerRoomListService = syncService.roomListService()
private val sessionDispatcher = dispatchers.io.limitedParallelism(64)
private val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-${sessionId}")
private val verificationService = RustSessionVerificationService()
- private val rustSyncService = RustSyncService(syncService, roomListService.stateFlow(), sessionCoroutineScope)
+ private val rustSyncService = RustSyncService(syncService, sessionCoroutineScope)
private val pushersService = RustPushersService(
client = client,
dispatchers = dispatchers,
)
private val notificationClient = client.notificationClient().use { builder ->
- builder.finish()
+ builder.filterByPushRules().finish()
}
- private val notificationService = RustNotificationService(notificationClient)
+ private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
private val isLoggingOut = AtomicBoolean(false)
@@ -122,15 +121,15 @@ class RustMatrixClient constructor(
}
}
- private val rustRoomSummaryDataSource: RustRoomSummaryDataSource =
- RustRoomSummaryDataSource(
- roomListService = roomListService,
+ private val rustRoomListService: RoomListService =
+ RustRoomListService(
+ innerRoomListService = innerRoomListService,
sessionCoroutineScope = sessionCoroutineScope,
dispatcher = sessionDispatcher,
)
- override val roomSummaryDataSource: RoomSummaryDataSource
- get() = rustRoomSummaryDataSource
+ override val roomListService: RoomListService
+ get() = rustRoomListService
private val rustMediaLoader = RustMediaLoader(baseCacheDirectory, dispatchers, client)
override val mediaLoader: MatrixMediaLoader
@@ -138,7 +137,7 @@ class RustMatrixClient constructor(
private val roomMembershipObserver = RoomMembershipObserver()
- private val roomContentForwarder = RoomContentForwarder(roomListService)
+ private val roomContentForwarder = RoomContentForwarder(innerRoomListService)
init {
client.setDelegate(clientDelegate)
@@ -147,35 +146,36 @@ class RustMatrixClient constructor(
if (syncState == SyncState.Running) {
onSlidingSyncUpdate()
}
- }.launchIn(sessionCoroutineScope)
+ }
+ .launchIn(sessionCoroutineScope)
}
- override suspend fun getRoom(roomId: RoomId): MatrixRoom? {
+ override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) {
// Check if already in memory...
var cachedPairOfRoom = pairOfRoom(roomId)
if (cachedPairOfRoom == null) {
//... otherwise, lets wait for the SS to load all rooms and check again.
- roomSummaryDataSource.awaitAllRoomsAreLoaded()
+ roomListService.allRooms().awaitLoaded()
cachedPairOfRoom = pairOfRoom(roomId)
}
- if (cachedPairOfRoom == null) return null
- val (roomListItem, fullRoom) = cachedPairOfRoom
- return RustMatrixRoom(
- sessionId = sessionId,
- roomListItem = roomListItem,
- innerRoom = fullRoom,
- sessionCoroutineScope = sessionCoroutineScope,
- coroutineDispatchers = dispatchers,
- systemClock = clock,
- roomContentForwarder = roomContentForwarder,
- sessionData = sessionStore.getSession(sessionId.value)!!,
- )
+ cachedPairOfRoom?.let { (roomListItem, fullRoom) ->
+ RustMatrixRoom(
+ sessionId = sessionId,
+ roomListItem = roomListItem,
+ innerRoom = fullRoom,
+ sessionCoroutineScope = sessionCoroutineScope,
+ coroutineDispatchers = dispatchers,
+ systemClock = clock,
+ roomContentForwarder = roomContentForwarder,
+ sessionData = sessionStore.getSession(sessionId.value)!!,
+ )
+ }
}
- private suspend fun pairOfRoom(roomId: RoomId): Pair? = withContext(sessionDispatcher) {
- val cachedRoomListItem = roomListService.roomOrNull(roomId.value)
+ private fun pairOfRoom(roomId: RoomId): Pair? {
+ val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value)
val fullRoom = cachedRoomListItem?.fullRoom()
- if (cachedRoomListItem == null || fullRoom == null) {
+ return if (cachedRoomListItem == null || fullRoom == null) {
Timber.d("No room cached for $roomId")
null
} else {
@@ -224,10 +224,11 @@ class RustMatrixClient constructor(
// Wait to receive the room back from the sync
withTimeout(30_000L) {
- roomSummaryDataSource.allRooms()
+ roomListService.allRooms().summaries
.filter { roomSummaries ->
roomSummaries.map { it.identifier() }.contains(roomId.value)
- }.first()
+ }
+ .first()
}
roomId
}
@@ -271,7 +272,7 @@ class RustMatrixClient constructor(
client.setDelegate(null)
verificationService.destroy()
syncService.destroy()
- roomListService.destroy()
+ innerRoomListService.destroy()
notificationClient.destroy()
client.destroy()
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt
new file mode 100644
index 0000000000..931133c266
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl
+
+import android.content.Context
+import io.element.android.libraries.core.coroutine.CoroutineDispatchers
+import io.element.android.libraries.di.ApplicationContext
+import io.element.android.libraries.network.useragent.UserAgentProvider
+import io.element.android.libraries.sessionstorage.api.SessionData
+import io.element.android.libraries.sessionstorage.api.SessionStore
+import io.element.android.services.toolbox.api.systemclock.SystemClock
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.withContext
+import org.matrix.rustcomponents.sdk.ClientBuilder
+import org.matrix.rustcomponents.sdk.Session
+import org.matrix.rustcomponents.sdk.use
+import java.io.File
+import javax.inject.Inject
+
+class RustMatrixClientFactory @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val baseDirectory: File,
+ private val appCoroutineScope: CoroutineScope,
+ private val coroutineDispatchers: CoroutineDispatchers,
+ private val sessionStore: SessionStore,
+ private val userAgentProvider: UserAgentProvider,
+ private val clock: SystemClock,
+) {
+
+ suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
+ val client = ClientBuilder()
+ .basePath(baseDirectory.absolutePath)
+ .homeserverUrl(sessionData.homeserverUrl)
+ .username(sessionData.userId)
+ .userAgent(userAgentProvider.provide())
+ // FIXME Quick and dirty fix for stopping version requests on startup https://github.com/matrix-org/matrix-rust-sdk/pull/1376
+ .serverVersions(listOf("v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5"))
+ .use { it.build() }
+
+ client.restoreSession(sessionData.toSession())
+
+ val syncService = client.syncService().finish()
+
+ RustMatrixClient(
+ client = client,
+ syncService = syncService,
+ sessionStore = sessionStore,
+ appCoroutineScope = appCoroutineScope,
+ dispatchers = coroutineDispatchers,
+ baseDirectory = baseDirectory,
+ baseCacheDirectory = context.cacheDir,
+ clock = clock,
+ )
+ }
+}
+
+private fun SessionData.toSession() = Session(
+ accessToken = accessToken,
+ refreshToken = refreshToken,
+ userId = userId,
+ deviceId = deviceId,
+ homeserverUrl = homeserverUrl,
+ slidingSyncProxy = slidingSyncProxy,
+)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt
index 1ba5063df9..b5115ffad4 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt
@@ -16,8 +16,8 @@
package io.element.android.libraries.matrix.impl.auth
-import io.element.android.libraries.matrix.api.auth.OidcConfig
// TODO Oidc
+// import io.element.android.libraries.matrix.api.auth.OidcConfig
// import org.matrix.rustcomponents.sdk.OidcClientMetadata
/*
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
index 0bc299d020..ba06891013 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt
@@ -16,33 +16,27 @@
package io.element.android.libraries.matrix.impl.auth
-import android.content.Context
+// TODO Oidc
+// import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.di.AppScope
-import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.core.SessionId
-import io.element.android.libraries.matrix.impl.RustMatrixClient
+import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
import io.element.android.libraries.matrix.impl.exception.mapClientException
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
-import io.element.android.services.toolbox.api.systemclock.SystemClock
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
-import org.matrix.rustcomponents.sdk.Client
-import org.matrix.rustcomponents.sdk.ClientBuilder
-// TODO Oidc
-// import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl
import org.matrix.rustcomponents.sdk.Session
import org.matrix.rustcomponents.sdk.use
import java.io.File
@@ -53,13 +47,11 @@ import org.matrix.rustcomponents.sdk.AuthenticationService as RustAuthentication
@ContributesBinding(AppScope::class)
@SingleIn(AppScope::class)
class RustMatrixAuthenticationService @Inject constructor(
- @ApplicationContext private val context: Context,
- private val baseDirectory: File,
- private val appCoroutineScope: CoroutineScope,
+ baseDirectory: File,
private val coroutineDispatchers: CoroutineDispatchers,
private val sessionStore: SessionStore,
- private val clock: SystemClock,
- private val userAgentProvider: UserAgentProvider,
+ userAgentProvider: UserAgentProvider,
+ private val rustMatrixClientFactory: RustMatrixClientFactory,
) : MatrixAuthenticationService {
private val authService: RustAuthenticationService = RustAuthenticationService(
@@ -84,16 +76,9 @@ class RustMatrixAuthenticationService @Inject constructor(
runCatching {
val sessionData = sessionStore.getSession(sessionId.value)
if (sessionData != null) {
- val client = ClientBuilder()
- .basePath(baseDirectory.absolutePath)
- .homeserverUrl(sessionData.homeserverUrl)
- .username(sessionData.userId)
- .userAgent(userAgentProvider.provide())
- .use { it.build() }
- client.restoreSession(sessionData.toSession())
- createMatrixClient(client)
+ rustMatrixClientFactory.create(sessionData)
} else {
- throw IllegalStateException("No session to restore with id $sessionId")
+ error("No session to restore with id $sessionId")
}
}.mapFailure { failure ->
failure.mapClientException()
@@ -181,30 +166,8 @@ class RustMatrixAuthenticationService @Inject constructor(
*/
}
- private suspend fun createMatrixClient(client: Client): MatrixClient {
- val syncService = client.syncService().finish()
- return RustMatrixClient(
- client = client,
- syncService = syncService,
- sessionStore = sessionStore,
- appCoroutineScope = appCoroutineScope,
- dispatchers = coroutineDispatchers,
- baseDirectory = baseDirectory,
- baseCacheDirectory = context.cacheDir,
- clock = clock,
- )
- }
}
-private fun SessionData.toSession() = Session(
- accessToken = accessToken,
- refreshToken = refreshToken,
- userId = userId,
- deviceId = deviceId,
- homeserverUrl = homeserverUrl,
- slidingSyncProxy = slidingSyncProxy,
-)
-
private fun Session.toSessionData() = SessionData(
userId = userId,
deviceId = deviceId,
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt
index bf260be6ec..dba1dbd0a3 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt
@@ -23,7 +23,7 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@Module
@@ -40,8 +40,8 @@ object SessionMatrixModule {
}
@Provides
- fun provideRoomSummaryDataSource(matrixClient: MatrixClient): RoomSummaryDataSource {
- return matrixClient.roomSummaryDataSource
+ fun providesRoomListService(matrixClient: MatrixClient): RoomListService {
+ return matrixClient.roomListService
}
@Provides
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt
index b66cec96fd..99b806ed4a 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt
@@ -17,7 +17,6 @@
package io.element.android.libraries.matrix.impl.media
import io.element.android.libraries.matrix.api.media.ImageInfo
-import org.matrix.rustcomponents.sdk.MediaSource
import org.matrix.rustcomponents.sdk.ImageInfo as RustImageInfo
fun RustImageInfo.map(): ImageInfo = ImageInfo(
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
index 07acb7fec5..0d9f794173 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
@@ -19,19 +19,29 @@ package io.element.android.libraries.matrix.impl.notification
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData
+import io.element.android.libraries.matrix.api.room.RoomMembershipState
+import io.element.android.services.toolbox.api.systemclock.SystemClock
+import org.matrix.rustcomponents.sdk.NotificationEvent
import org.matrix.rustcomponents.sdk.NotificationItem
import org.matrix.rustcomponents.sdk.use
-class NotificationMapper {
- private val timelineEventMapper = TimelineEventMapper()
+class NotificationMapper(
+ sessionId: SessionId,
+ private val clock: SystemClock,
+) {
+ private val notificationContentMapper = NotificationContentMapper(sessionId)
- fun map(roomId: RoomId, notificationItem: NotificationItem): NotificationData {
+ fun map(
+ eventId: EventId,
+ roomId: RoomId,
+ notificationItem: NotificationItem
+ ): NotificationData {
return notificationItem.use { item ->
NotificationData(
- senderId = UserId(item.event.senderId()),
- eventId = EventId(item.event.eventId()),
+ eventId = eventId,
roomId = roomId,
senderAvatarUrl = item.senderInfo.avatarUrl,
senderDisplayName = item.senderInfo.displayName,
@@ -39,9 +49,28 @@ class NotificationMapper {
roomDisplayName = item.roomInfo.displayName,
isDirect = item.roomInfo.isDirect,
isEncrypted = item.roomInfo.isEncrypted.orFalse(),
- isNoisy = item.isNoisy,
- event = item.event.use { event -> timelineEventMapper.map(event) }
+ isNoisy = item.isNoisy.orFalse(),
+ timestamp = item.timestamp() ?: clock.epochMillis(),
+ content = item.event.use { notificationContentMapper.map(it) },
+ contentUrl = null,
)
}
}
}
+
+class NotificationContentMapper(private val sessionId: SessionId) {
+ private val timelineEventToNotificationContentMapper = TimelineEventToNotificationContentMapper()
+
+ fun map(notificationEvent: NotificationEvent): NotificationContent =
+ when (notificationEvent) {
+ is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(notificationEvent.event)
+ is NotificationEvent.Invite -> NotificationContent.StateEvent.RoomMemberContent(
+ userId = sessionId.value,
+ membershipState = RoomMembershipState.INVITE,
+ )
+ }
+}
+
+private fun NotificationItem.timestamp(): Long? {
+ return (this.event as? NotificationEvent.Timeline)?.event?.timestamp()?.toLong()
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt
index 92c996049e..0f3aebd049 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt
@@ -16,29 +16,34 @@
package io.element.android.libraries.matrix.impl.notification
+import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.notification.NotificationService
+import io.element.android.services.toolbox.api.systemclock.SystemClock
+import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.NotificationClient
import org.matrix.rustcomponents.sdk.use
class RustNotificationService(
+ sessionId: SessionId,
private val notificationClient: NotificationClient,
+ private val dispatchers: CoroutineDispatchers,
+ clock: SystemClock,
) : NotificationService {
- private val notificationMapper: NotificationMapper = NotificationMapper()
+ private val notificationMapper: NotificationMapper = NotificationMapper(sessionId, clock)
- override fun getNotification(
+ override suspend fun getNotification(
userId: SessionId,
roomId: RoomId,
eventId: EventId,
- filterByPushRules: Boolean,
- ): Result {
- return runCatching {
+ ): Result = withContext(dispatchers.io) {
+ runCatching {
val item = notificationClient.getNotification(roomId.value, eventId.value)
item?.use {
- notificationMapper.map(roomId, it)
+ notificationMapper.map(eventId, roomId, it)
}
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
similarity index 85%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt
rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
index f7d4a00188..e30e57113d 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
@@ -16,8 +16,8 @@
package io.element.android.libraries.matrix.impl.notification
+import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.notification.NotificationContent
-import io.element.android.libraries.matrix.api.notification.NotificationEvent
import io.element.android.libraries.matrix.impl.room.RoomMemberMapper
import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
import org.matrix.rustcomponents.sdk.MessageLikeEventContent
@@ -27,22 +27,20 @@ import org.matrix.rustcomponents.sdk.TimelineEventType
import org.matrix.rustcomponents.sdk.use
import javax.inject.Inject
-class TimelineEventMapper @Inject constructor() {
+class TimelineEventToNotificationContentMapper @Inject constructor() {
- fun map(timelineEvent: TimelineEvent): NotificationEvent {
+ fun map(timelineEvent: TimelineEvent): NotificationContent {
return timelineEvent.use {
- NotificationEvent(
- timestamp = it.timestamp().toLong(),
- content = it.eventType().toContent(),
- contentUrl = null // TODO it.eventType().toContentUrl(),
- )
+ timelineEvent.eventType().use { eventType ->
+ eventType.toContent(senderId = UserId(timelineEvent.senderId()))
+ }
}
}
}
-private fun TimelineEventType.toContent(): NotificationContent {
+private fun TimelineEventType.toContent(senderId: UserId): NotificationContent {
return when (this) {
- is TimelineEventType.MessageLike -> content.toContent()
+ is TimelineEventType.MessageLike -> content.toContent(senderId)
is TimelineEventType.State -> content.toContent()
}
}
@@ -75,9 +73,9 @@ private fun StateEventContent.toContent(): NotificationContent.StateEvent {
}
}
-private fun MessageLikeEventContent.toContent(): NotificationContent.MessageLike {
+private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationContent.MessageLike {
return use {
- when (it) {
+ when (this) {
MessageLikeEventContent.CallAnswer -> NotificationContent.MessageLike.CallAnswer
MessageLikeEventContent.CallCandidates -> NotificationContent.MessageLike.CallCandidates
MessageLikeEventContent.CallHangup -> NotificationContent.MessageLike.CallHangup
@@ -89,10 +87,10 @@ private fun MessageLikeEventContent.toContent(): NotificationContent.MessageLike
MessageLikeEventContent.KeyVerificationMac -> NotificationContent.MessageLike.KeyVerificationMac
MessageLikeEventContent.KeyVerificationReady -> NotificationContent.MessageLike.KeyVerificationReady
MessageLikeEventContent.KeyVerificationStart -> NotificationContent.MessageLike.KeyVerificationStart
- is MessageLikeEventContent.ReactionContent -> NotificationContent.MessageLike.ReactionContent(it.relatedEventId)
+ is MessageLikeEventContent.ReactionContent -> NotificationContent.MessageLike.ReactionContent(relatedEventId)
MessageLikeEventContent.RoomEncrypted -> NotificationContent.MessageLike.RoomEncrypted
is MessageLikeEventContent.RoomMessage -> {
- NotificationContent.MessageLike.RoomMessage(EventMessageMapper().mapMessageType(it.messageType))
+ NotificationContent.MessageLike.RoomMessage(senderId, EventMessageMapper().mapMessageType(messageType))
}
MessageLikeEventContent.RoomRedaction -> NotificationContent.MessageLike.RoomRedaction
MessageLikeEventContent.Sticker -> NotificationContent.MessageLike.Sticker
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt
new file mode 100644
index 0000000000..c3098bdcb0
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.poll
+
+import io.element.android.libraries.matrix.api.poll.PollAnswer
+import org.matrix.rustcomponents.sdk.PollAnswer as RustPollAnswer
+
+fun RustPollAnswer.map(): PollAnswer = PollAnswer(
+ id = id,
+ text = text,
+)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt
new file mode 100644
index 0000000000..bde49464ad
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.poll
+
+import io.element.android.libraries.matrix.api.poll.PollKind
+import org.matrix.rustcomponents.sdk.PollKind as RustPollKind
+
+fun RustPollKind.map(): PollKind = when (this) {
+ RustPollKind.DISCLOSED -> PollKind.Disclosed
+ RustPollKind.UNDISCLOSED -> PollKind.Undisclosed
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
index 4e2d63d091..8ee0361ace 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
@@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.parallelMap
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.ForwardEventException
+import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.Room
@@ -80,6 +81,6 @@ class RoomContentForwarder(
}
private object NoOpTimelineListener : TimelineListener {
- override fun onUpdate(diff: TimelineDiff) = Unit
+ override fun onUpdate(diff: List) = Unit
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
index 16434aa3c8..dc74a273f4 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
@@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope
+import io.element.android.libraries.core.coroutine.parallelMap
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomId
@@ -40,9 +41,6 @@ import io.element.android.libraries.matrix.impl.core.toProgressWatcher
import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.room.location.toInner
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
-import io.element.android.libraries.matrix.impl.timeline.backPaginationStatusFlow
-import io.element.android.libraries.matrix.impl.timeline.eventOrigin
-import io.element.android.libraries.matrix.impl.timeline.timelineDiffFlow
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope
@@ -51,11 +49,7 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import org.matrix.rustcomponents.sdk.EventItemOrigin
import org.matrix.rustcomponents.sdk.RequiredState
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
@@ -88,7 +82,6 @@ class RustMatrixRoom(
private val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId")
private val _membersStateFlow = MutableStateFlow(MatrixRoomMembersState.Unknown)
- private val isInit = MutableStateFlow(false)
private val _syncUpdateFlow = MutableStateFlow(0L)
private val _timeline by lazy {
RustMatrixTimeline(
@@ -97,6 +90,7 @@ class RustMatrixRoom(
roomCoroutineScope = roomCoroutineScope,
dispatcher = roomDispatcher,
lastLoginTimestamp = sessionData.loginTimestamp,
+ onNewSyncedEvent = { _syncUpdateFlow.value = systemClock.epochMillis() }
)
}
@@ -106,8 +100,7 @@ class RustMatrixRoom(
override val timeline: MatrixTimeline = _timeline
- override fun open(): Result {
- if (isInit.value) return Result.failure(IllegalStateException("Listener already registered"))
+ override fun subscribeToSync() {
val settings = RoomSubscription(
requiredState = listOf(
RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""),
@@ -118,35 +111,16 @@ class RustMatrixRoom(
timelineLimit = null
)
roomListItem.subscribe(settings)
- roomCoroutineScope.launch(roomDispatcher) {
- innerRoom.timelineDiffFlow { initialList ->
- _timeline.postItems(initialList)
- }.onEach { diff ->
- if (diff.eventOrigin() == EventItemOrigin.SYNC) {
- _syncUpdateFlow.value = systemClock.epochMillis()
- }
- _timeline.postDiff(diff)
- }.launchIn(this)
-
- innerRoom.backPaginationStatusFlow()
- .onEach {
- _timeline.postPaginationStatus(it)
- }.launchIn(this)
-
- fetchMembers()
- }
- isInit.value = true
- return Result.success(Unit)
}
- override fun close() {
- if (isInit.value) {
- isInit.value = false
- roomCoroutineScope.cancel()
- roomListItem.unsubscribe()
- innerRoom.destroy()
- roomListItem.destroy()
- }
+ override fun unsubscribeFromSync() {
+ roomListItem.unsubscribe()
+ }
+
+ override fun destroy() {
+ roomCoroutineScope.cancel()
+ innerRoom.destroy()
+ roomListItem.destroy()
}
override val name: String?
@@ -166,7 +140,7 @@ class RustMatrixRoom(
override val avatarUrl: String?
get() {
- return innerRoom.avatarUrl()
+ return roomListItem.avatarUrl() ?: innerRoom.avatarUrl()
}
override val isEncrypted: Boolean
@@ -195,7 +169,7 @@ class RustMatrixRoom(
val currentMembers = currentState.roomMembers()
_membersStateFlow.value = MatrixRoomMembersState.Pending(prevRoomMembers = currentMembers)
runCatching {
- innerRoom.members().map(RoomMemberMapper::map)
+ innerRoom.members().parallelMap(RoomMemberMapper::map)
}.map {
_membersStateFlow.value = MatrixRoomMembersState.Ready(it)
}.onFailure {
@@ -276,6 +250,12 @@ class RustMatrixRoom(
}
}
+ override suspend fun canUserRedact(userId: UserId): Result {
+ return runCatching {
+ innerRoom.canUserRedact(userId.value)
+ }
+ }
+
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result {
return runCatching {
innerRoom.canUserSendState(userId.value, type.map())
@@ -363,12 +343,6 @@ class RustMatrixRoom(
}
}
- private suspend fun fetchMembers() = withContext(roomDispatcher) {
- runCatching {
- innerRoom.fetchMembers()
- }
- }
-
override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result = withContext(roomDispatcher) {
runCatching {
innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason)
@@ -396,13 +370,15 @@ class RustMatrixRoom(
)
}
}
-}
-//TODO handle cancellation, need refactoring of how we are catching errors
-private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result {
- return runCatching {
- handle().use {
- it.join()
+ //TODO handle cancellation, need refactoring of how we are catching errors
+ private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result {
+ return runCatching {
+ handle().use {
+ it.join()
+ }
}
}
}
+
+
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt
deleted file mode 100644
index efdbcf34ad..0000000000
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.libraries.matrix.impl.room
-
-import io.element.android.libraries.matrix.api.room.RoomSummary
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import org.matrix.rustcomponents.sdk.RoomList
-import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
-import org.matrix.rustcomponents.sdk.RoomListException
-import org.matrix.rustcomponents.sdk.RoomListInput
-import org.matrix.rustcomponents.sdk.RoomListLoadingState
-import org.matrix.rustcomponents.sdk.RoomListRange
-import org.matrix.rustcomponents.sdk.RoomListService
-import org.matrix.rustcomponents.sdk.RoomListServiceState
-import timber.log.Timber
-
-internal class RustRoomSummaryDataSource(
- private val roomListService: RoomListService,
- private val sessionCoroutineScope: CoroutineScope,
- dispatcher: CoroutineDispatcher,
- roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
-) : RoomSummaryDataSource {
-
- private val allRooms = MutableStateFlow>(emptyList())
- private val inviteRooms = MutableStateFlow>(emptyList())
-
- private val allRoomsLoadingState: MutableStateFlow = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded)
- private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, roomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = false)
- private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, roomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = true)
-
- init {
- sessionCoroutineScope.launch(dispatcher) {
- val allRooms = roomListService.allRooms()
- allRooms
- .observeEntriesWithProcessor(allRoomsListProcessor)
- .launchIn(this)
-
- allRooms
- .loadingStateFlow()
- .map { it.toRoomSummaryDataSourceLoadingState() }
- .onEach {
- allRoomsLoadingState.value = it
- }.launchIn(this)
-
- launch {
- // Wait until running, as invites is only available after that
- roomListService.stateFlow().first {
- it == RoomListServiceState.RUNNING
- }
- roomListService.invites()
- .observeEntriesWithProcessor(inviteRoomsListProcessor)
- .launchIn(this)
- }
- }
- }
-
- override fun allRooms(): StateFlow> {
- return allRooms
- }
-
- override fun inviteRooms(): StateFlow> {
- return inviteRooms
- }
-
- override fun allRoomsLoadingState(): StateFlow {
- return allRoomsLoadingState
- }
-
- override fun updateAllRoomsVisibleRange(range: IntRange) {
- Timber.v("setVisibleRange=$range")
- sessionCoroutineScope.launch {
- try {
- val ranges = listOf(RoomListRange(range.first.toUInt(), range.last.toUInt()))
- roomListService.applyInput(
- RoomListInput.Viewport(ranges)
- )
- } catch (exception: RoomListException) {
- Timber.e(exception, "Failed updating visible range")
- }
- }
- }
-}
-
-private fun RoomListLoadingState.toRoomSummaryDataSourceLoadingState(): RoomSummaryDataSource.LoadingState {
- return when (this) {
- is RoomListLoadingState.Loaded -> RoomSummaryDataSource.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0)
- is RoomListLoadingState.NotLoaded -> RoomSummaryDataSource.LoadingState.NotLoaded
- }
-}
-
-private fun RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow {
- return entriesFlow { roomListEntries ->
- processor.postEntries(roomListEntries)
- }.onEach { update ->
- processor.postUpdate(update)
- }
-}
-
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt
similarity index 76%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt
rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt
index 84c4eeaefb..8d96990a9e 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.impl.room
+package io.element.android.libraries.matrix.impl.roomlist
+import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.catch
import org.matrix.rustcomponents.sdk.RoomList
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
import org.matrix.rustcomponents.sdk.RoomListEntry
-import org.matrix.rustcomponents.sdk.RoomListException
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomListLoadingState
import org.matrix.rustcomponents.sdk.RoomListLoadingStateListener
@@ -42,31 +43,34 @@ fun RoomList.loadingStateFlow(): Flow =
}
}
val result = loadingState(listener)
- send(result.state)
+ try {
+ send(result.state)
+ } catch (exception: Exception) {
+ Timber.d("loadingStateFlow() initialState failed.")
+ }
result.stateStream
+ }.catch {
+ Timber.d(it, "loadingStateFlow() failed")
}.buffer(Channel.UNLIMITED)
-fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow =
+fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> =
mxCallbackFlow {
val listener = object : RoomListEntriesListener {
- override fun onUpdate(roomEntriesUpdate: RoomListEntriesUpdate) {
+ override fun onUpdate(roomEntriesUpdate: List) {
trySendBlocking(roomEntriesUpdate)
}
}
val result = entries(listener)
- onInitialList(result.entries)
+ try {
+ onInitialList(result.entries)
+ } catch (exception: Exception) {
+ Timber.d("entriesFlow() onInitialList failed.")
+ }
result.entriesStream
+ }.catch {
+ Timber.d(it, "entriesFlow() failed")
}.buffer(Channel.UNLIMITED)
-fun RoomListService.roomOrNull(roomId: String): RoomListItem? {
- return try {
- room(roomId)
- } catch (exception: RoomListException) {
- Timber.d(exception, "Failed finding room with id=$roomId.")
- return null
- }
-}
-
fun RoomListService.stateFlow(): Flow =
mxCallbackFlow {
val listener = object : RoomListServiceStateListener {
@@ -74,5 +78,16 @@ fun RoomListService.stateFlow(): Flow =
trySendBlocking(state)
}
}
- state(listener)
+ tryOrNull {
+ state(listener)
+ }
}.buffer(Channel.UNLIMITED)
+
+fun RoomListService.roomOrNull(roomId: String): RoomListItem? {
+ return try {
+ room(roomId)
+ } catch (exception: Exception) {
+ Timber.d(exception, "Failed finding room with id=$roomId.")
+ return null
+ }
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt
similarity index 89%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt
rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt
index 7dd7bf4581..b57eb892e0 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.impl.room
+package io.element.android.libraries.matrix.impl.roomlist
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
+import io.element.android.libraries.matrix.impl.room.RoomMemberMapper
import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt
similarity index 93%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt
rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt
index a8ab4cb807..9a67ff1f30 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.impl.room
+package io.element.android.libraries.matrix.impl.roomlist
import io.element.android.libraries.core.coroutine.parallelMap
-import io.element.android.libraries.matrix.api.room.RoomSummary
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex
@@ -50,12 +50,14 @@ class RoomSummaryListProcessor(
initLatch.complete(Unit)
}
- suspend fun postUpdate(update: RoomListEntriesUpdate) {
+ suspend fun postUpdate(updates: List) {
// Makes sure to process first entries before update.
initLatch.await()
updateRoomSummaries {
- Timber.v("Update rooms from postUpdate ($update) on ${Thread.currentThread()}")
- applyUpdate(update)
+ Timber.v("Update rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
+ updates.forEach { update ->
+ applyUpdate(update)
+ }
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt
new file mode 100644
index 0000000000..481b38dd9b
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.roomlist
+
+import io.element.android.libraries.matrix.api.roomlist.RoomList
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Simple implementation of [RoomList] where state flows are provided through constructor.
+ */
+class RustRoomList(
+ override val summaries: StateFlow>,
+ override val loadingState: StateFlow
+) : RoomList
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt
new file mode 100644
index 0000000000..bf66bade3b
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.roomlist
+
+import io.element.android.libraries.matrix.api.roomlist.RoomList
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
+import org.matrix.rustcomponents.sdk.RoomListException
+import org.matrix.rustcomponents.sdk.RoomListInput
+import org.matrix.rustcomponents.sdk.RoomListLoadingState
+import org.matrix.rustcomponents.sdk.RoomListRange
+import org.matrix.rustcomponents.sdk.RoomListServiceState
+import timber.log.Timber
+import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
+
+class RustRoomListService(
+ private val innerRoomListService: InnerRustRoomListService,
+ private val sessionCoroutineScope: CoroutineScope,
+ dispatcher: CoroutineDispatcher,
+ roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
+) : RoomListService {
+
+ private val allRooms = MutableStateFlow>(emptyList())
+ private val inviteRooms = MutableStateFlow>(emptyList())
+
+ private val allRoomsLoadingState: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded)
+ private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, innerRoomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = false)
+ private val invitesLoadingState: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded)
+ private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, innerRoomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = true)
+
+ init {
+ sessionCoroutineScope.launch(dispatcher) {
+ val allRooms = innerRoomListService.allRooms()
+ allRooms
+ .observeEntriesWithProcessor(allRoomsListProcessor)
+ .launchIn(this)
+ allRooms
+ .observeLoadingState(allRoomsLoadingState)
+ .launchIn(this)
+
+
+ launch {
+ // Wait until running, as invites is only available after that
+ innerRoomListService.stateFlow().first {
+ it == RoomListServiceState.RUNNING
+ }
+ val invites = innerRoomListService.invites()
+ invites
+ .observeEntriesWithProcessor(inviteRoomsListProcessor)
+ .launchIn(this)
+ invites
+ .observeLoadingState(invitesLoadingState)
+ .launchIn(this)
+
+ }
+ }
+ }
+
+ override fun allRooms(): RoomList {
+ return RustRoomList(allRooms, allRoomsLoadingState)
+ }
+
+ override fun invites(): RoomList {
+ return RustRoomList(inviteRooms, invitesLoadingState)
+ }
+
+ override fun updateAllRoomsVisibleRange(range: IntRange) {
+ Timber.v("setVisibleRange=$range")
+ sessionCoroutineScope.launch {
+ try {
+ val ranges = listOf(RoomListRange(range.first.toUInt(), range.last.toUInt()))
+ innerRoomListService.applyInput(
+ RoomListInput.Viewport(ranges)
+ )
+ } catch (exception: RoomListException) {
+ Timber.e(exception, "Failed updating visible range")
+ }
+ }
+ }
+
+ override val state: StateFlow =
+ innerRoomListService.stateFlow()
+ .map { it.toRoomListState() }
+ .onEach { state ->
+ Timber.d("RoomList state=$state")
+ }
+ .distinctUntilChanged()
+ .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
+}
+
+private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState {
+ return when (this) {
+ is RoomListLoadingState.Loaded -> RoomList.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0)
+ RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded
+ }
+}
+
+private fun RoomListServiceState.toRoomListState(): RoomListService.State {
+ return when (this) {
+ RoomListServiceState.INIT,
+ RoomListServiceState.SETTING_UP -> RoomListService.State.Idle
+ RoomListServiceState.RUNNING -> RoomListService.State.Running
+ RoomListServiceState.ERROR -> RoomListService.State.Error
+ RoomListServiceState.TERMINATED -> RoomListService.State.Terminated
+ }
+}
+
+private fun org.matrix.rustcomponents.sdk.RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow> {
+ return entriesFlow { roomListEntries ->
+ processor.postEntries(roomListEntries)
+ }.onEach { update ->
+ processor.postUpdate(update)
+ }
+}
+
+private fun org.matrix.rustcomponents.sdk.RoomList.observeLoadingState(stateFlow: MutableStateFlow): Flow {
+ return loadingStateFlow()
+ .map { it.toLoadingState() }
+ .onEach {
+ stateFlow.value = it
+ }
+}
+
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt
index 51228231f9..ae90f9fc44 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt
@@ -17,19 +17,8 @@
package io.element.android.libraries.matrix.impl.sync
import io.element.android.libraries.matrix.api.sync.SyncState
-import org.matrix.rustcomponents.sdk.RoomListServiceState
import org.matrix.rustcomponents.sdk.SyncServiceState
-internal fun RoomListServiceState.toSyncState(): SyncState {
- return when (this) {
- RoomListServiceState.INIT,
- RoomListServiceState.SETTING_UP -> SyncState.Idle
- RoomListServiceState.RUNNING -> SyncState.Running
- RoomListServiceState.ERROR -> SyncState.Error
- RoomListServiceState.TERMINATED -> SyncState.Terminated
- }
-}
-
internal fun SyncServiceState.toSyncState(): SyncState {
return when (this) {
SyncServiceState.IDLE -> SyncState.Idle
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt
index ca40ca400c..932da42afb 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt
@@ -19,38 +19,40 @@ package io.element.android.libraries.matrix.impl.sync
import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
-import org.matrix.rustcomponents.sdk.RoomListServiceState
import org.matrix.rustcomponents.sdk.SyncServiceInterface
+import org.matrix.rustcomponents.sdk.SyncServiceState
import timber.log.Timber
class RustSyncService(
private val innerSyncService: SyncServiceInterface,
- roomListStateFlow: Flow,
sessionCoroutineScope: CoroutineScope
) : SyncService {
override suspend fun startSync() = runCatching {
- Timber.v("Start sync")
+ Timber.i("Start sync")
innerSyncService.start()
+ }.onFailure {
+ Timber.d("Start sync failed: $it")
}
- override fun stopSync() = runCatching {
- Timber.v("Stop sync")
- innerSyncService.pause()
+ override suspend fun stopSync() = runCatching {
+ Timber.i("Stop sync")
+ innerSyncService.stop()
+ }.onFailure {
+ Timber.d("Stop sync failed: $it")
}
override val syncState: StateFlow =
- roomListStateFlow
- .map(RoomListServiceState::toSyncState)
+ innerSyncService.stateFlow()
+ .map(SyncServiceState::toSyncState)
.onEach { state ->
- Timber.v("Sync state=$state")
+ Timber.i("Sync state=$state")
}
.distinctUntilChanged()
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, SyncState.Idle)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt
index 36dabb71f3..c9e38ec7d4 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt
@@ -16,21 +16,24 @@
package io.element.android.libraries.matrix.impl.sync
+import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
-import org.matrix.rustcomponents.sdk.SyncService
+import org.matrix.rustcomponents.sdk.SyncServiceInterface
import org.matrix.rustcomponents.sdk.SyncServiceState
import org.matrix.rustcomponents.sdk.SyncServiceStateObserver
-fun SyncService.stateFlow(): Flow =
+fun SyncServiceInterface.stateFlow(): Flow =
mxCallbackFlow {
val listener = object : SyncServiceStateObserver {
override fun onUpdate(state: SyncServiceState) {
trySendBlocking(state)
}
}
- state(listener)
+ tryOrNull {
+ state(listener)
+ }
}.buffer(Channel.UNLIMITED)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt
index b14d459697..fd880e7fe3 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt
@@ -23,6 +23,7 @@ import kotlinx.coroutines.sync.withLock
import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
+import timber.log.Timber
internal class MatrixTimelineDiffProcessor(
private val timelineItems: MutableStateFlow>,
@@ -33,14 +34,18 @@ internal class MatrixTimelineDiffProcessor(
suspend fun postItems(items: List) {
updateTimelineItems {
+ Timber.v("Update timeline items from postItems (with ${items.size} items) on ${Thread.currentThread()}")
val mappedItems = items.map { it.asMatrixTimelineItem() }
addAll(0, mappedItems)
}
}
- suspend fun postDiff(diff: TimelineDiff) {
+ suspend fun postDiffs(diffs: List) {
updateTimelineItems {
- applyDiff(diff)
+ Timber.v("Update timeline items from postDiffs (with ${diffs.size} items) on ${Thread.currentThread()}")
+ diffs.forEach { diff ->
+ applyDiff(diff)
+ }
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt
index d6febb32dc..bddd2bc872 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt
@@ -16,28 +16,47 @@
package io.element.android.libraries.matrix.impl.timeline
+import io.element.android.libraries.core.data.tryOrNull
+import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
+import io.element.android.libraries.matrix.impl.util.destroyAll
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.catch
import org.matrix.rustcomponents.sdk.BackPaginationStatus
import org.matrix.rustcomponents.sdk.BackPaginationStatusListener
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
import org.matrix.rustcomponents.sdk.TimelineListener
+import timber.log.Timber
-internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow =
- mxCallbackFlow {
+internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow> =
+ callbackFlow {
val listener = object : TimelineListener {
- override fun onUpdate(diff: TimelineDiff) {
+ override fun onUpdate(diff: List) {
trySendBlocking(diff)
}
}
+ val roomId = id()
+ Timber.d("Open timelineDiffFlow for room $roomId")
val result = addTimelineListener(listener)
- onInitialList(result.items)
- result.itemsStream
+ try {
+ onInitialList(result.items)
+ } catch (exception: Exception) {
+ Timber.d(exception, "Catch failure in timelineDiffFlow of room $roomId")
+ }
+ awaitClose {
+ Timber.d("Close timelineDiffFlow for room $roomId")
+ result.itemsStream.cancelAndDestroy()
+ result.items.destroyAll()
+ }
+ }.catch {
+ Timber.d(it, "timelineDiffFlow() failed")
}.buffer(Channel.UNLIMITED)
internal fun Room.backPaginationStatusFlow(): Flow =
@@ -47,5 +66,7 @@ internal fun Room.backPaginationStatusFlow(): Flow =
trySendBlocking(status)
}
}
- subscribeToBackPaginationStatus(listener)
+ tryOrNull {
+ subscribeToBackPaginationStatus(listener)
+ }
}.buffer(Channel.UNLIMITED)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
index e213fb623c..aa8427608d 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
@@ -26,28 +26,33 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessage
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
-import kotlinx.coroutines.CompletableDeferred
import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor
+import io.element.android.libraries.matrix.impl.util.TaskHandleBag
+import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.getAndUpdate
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.sample
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.BackPaginationStatus
+import org.matrix.rustcomponents.sdk.EventItemOrigin
import org.matrix.rustcomponents.sdk.PaginationOptions
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem
import timber.log.Timber
-import java.util.concurrent.atomic.AtomicBoolean
import java.util.Date
+import java.util.concurrent.atomic.AtomicBoolean
private const val INITIAL_MAX_SIZE = 50
@@ -57,6 +62,7 @@ class RustMatrixTimeline(
private val innerRoom: Room,
private val dispatcher: CoroutineDispatcher,
private val lastLoginTimestamp: Date?,
+ private val onNewSyncedEvent: () -> Unit,
) : MatrixTimeline {
private val initLatch = CompletableDeferred()
@@ -73,6 +79,7 @@ class RustMatrixTimeline(
lastLoginTimestamp = lastLoginTimestamp,
isRoomEncrypted = matrixRoom.isEncrypted,
paginationStateFlow = _paginationState,
+ dispatcher = dispatcher,
)
private val timelineItemFactory = MatrixTimelineItemMapper(
@@ -93,27 +100,60 @@ class RustMatrixTimeline(
override val paginationState: StateFlow = _paginationState.asStateFlow()
- @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
- override val timelineItems: Flow> = _timelineItems.sample(50)
- .mapLatest { items ->
- encryptedHistoryPostProcessor.process(items)
- }
+ init {
+ Timber.d("Initialize timeline for room ${matrixRoom.roomId}")
- internal suspend fun postItems(items: List) {
+ val taskHandleBag = TaskHandleBag()
+ roomCoroutineScope.launch(dispatcher) {
+ innerRoom.timelineDiffFlow { initialList ->
+ postItems(initialList)
+ }.onEach { diffs ->
+ if (diffs.any { diff -> diff.eventOrigin() == EventItemOrigin.SYNC }) {
+ onNewSyncedEvent()
+ }
+ postDiffs(diffs)
+ }.launchIn(this)
+
+ innerRoom.backPaginationStatusFlow()
+ .onEach {
+ postPaginationStatus(it)
+ }
+ .launchIn(this)
+
+ taskHandleBag += fetchMembers().getOrNull()
+ }.invokeOnCompletion {
+ taskHandleBag.dispose()
+ }
+ }
+
+ private suspend fun fetchMembers() = withContext(dispatcher) {
+ initLatch.await()
+ runCatching {
+ innerRoom.fetchMembers()
+ }
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override val timelineItems: Flow> = _timelineItems.mapLatest { items ->
+ encryptedHistoryPostProcessor.process(items)
+ }
+
+ private suspend fun postItems(items: List) = coroutineScope {
// Split the initial items in multiple list as there is no pagination in the cached data, so we can post timelineItems asap.
items.chunked(INITIAL_MAX_SIZE).reversed().forEach {
+ ensureActive()
timelineDiffProcessor.postItems(it)
}
isInit.set(true)
initLatch.complete(Unit)
}
- internal suspend fun postDiff(timelineDiff: TimelineDiff) {
+ private suspend fun postDiffs(diffs: List) {
initLatch.await()
- timelineDiffProcessor.postDiff(timelineDiff)
+ timelineDiffProcessor.postDiffs(diffs)
}
- internal fun postPaginationStatus(status: BackPaginationStatus) {
+ private fun postPaginationStatus(status: BackPaginationStatus) {
_paginationState.getAndUpdate { currentPaginationState ->
if (hasEncryptionHistoryBanner()) {
return@getAndUpdate currentPaginationState.copy(
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
index 9f4df1f6b3..330a06da62 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
@@ -42,16 +42,18 @@ import org.matrix.rustcomponents.sdk.MessageType as RustMessageType
class EventMessageMapper {
+ private val timelineEventContentMapper by lazy { TimelineEventContentMapper() }
+
fun map(message: Message): MessageContent = message.use {
val type = it.msgtype().use(this::mapMessageType)
val inReplyToId = it.inReplyTo()?.eventId?.let(::EventId)
- val inReplyToEvent: InReplyTo? = (it.inReplyTo()?.event)?.use { details ->
+ val inReplyToEvent: InReplyTo? = it.inReplyTo()?.event?.use { details ->
when (details) {
is RepliedToEventDetails.Ready -> {
val senderProfile = details.senderProfile as? ProfileDetails.Ready
InReplyTo.Ready(
eventId = inReplyToId!!,
- content = map(details.message),
+ content = timelineEventContentMapper.map(details.content),
senderId = UserId(details.sender),
senderDisplayName = senderProfile?.displayName,
senderAvatarUrl = senderProfile?.avatarUrl,
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt
index 359b9ecdef..21e7d51638 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt
@@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
+import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender
import org.matrix.rustcomponents.sdk.Reaction
import org.matrix.rustcomponents.sdk.EventItemOrigin as RustEventItemOrigin
import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState
@@ -81,8 +82,12 @@ private fun List?.map(): List {
return this?.map {
EventReaction(
key = it.key,
- count = it.count.toLong(),
- senderIds = it.senders.map { sender -> UserId(sender.senderId) }
+ senders = it.senders.map { sender ->
+ ReactionSender(
+ senderId = UserId(sender.senderId),
+ timestamp = sender.timestamp.toLong()
+ )
+ }
)
} ?: emptyList()
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt
index 33727c15d4..7ee1d1490d 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt
@@ -22,6 +22,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
+import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
+import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
@@ -30,6 +32,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StickerConten
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
import io.element.android.libraries.matrix.impl.media.map
+import io.element.android.libraries.matrix.impl.poll.map
import org.matrix.rustcomponents.sdk.TimelineItemContent
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
import org.matrix.rustcomponents.sdk.EncryptedMessage as RustEncryptedMessage
@@ -91,11 +94,27 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
url = kind.url,
)
}
+ is TimelineItemContentKind.Poll -> {
+ PollContent(
+ question = kind.question,
+ kind = kind.kind.map(),
+ maxSelections = kind.maxSelections,
+ answers = kind.answers.map { answer -> answer.map() },
+ votes = kind.votes.mapValues { vote ->
+ vote.value.map { userId -> UserId(userId) }
+ },
+ endTime = kind.endTime,
+ )
+ }
+ is TimelineItemContentKind.PollEnd -> {
+ PollEndContent(startEventId = kind.startEventId)
+ }
is TimelineItemContentKind.UnableToDecrypt -> {
UnableToDecryptContent(
data = kind.msg.map()
)
}
+ else -> UnknownContent
}
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt
index ca5c7342f8..b273bef21b 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt
@@ -19,19 +19,23 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
+import kotlinx.coroutines.withContext
+import timber.log.Timber
import java.util.Date
-import java.util.UUID
class TimelineEncryptedHistoryPostProcessor(
+ private val dispatcher: CoroutineDispatcher,
private val lastLoginTimestamp: Date?,
private val isRoomEncrypted: Boolean,
private val paginationStateFlow: MutableStateFlow,
) {
- fun process(items: List): List {
- if (!isRoomEncrypted || lastLoginTimestamp == null) return items
+ suspend fun process(items: List): List = withContext(dispatcher) {
+ Timber.d("Process on Thread=${Thread.currentThread()}")
+ if (!isRoomEncrypted || lastLoginTimestamp == null) return@withContext items
val filteredItems = replaceWithEncryptionHistoryBannerIfNeeded(items)
// Disable back pagination
@@ -44,7 +48,7 @@ class TimelineEncryptedHistoryPostProcessor(
)
}
}
- return filteredItems
+ filteredItems
}
private fun replaceWithEncryptionHistoryBannerIfNeeded(list: List): List {
@@ -70,5 +74,4 @@ class TimelineEncryptedHistoryPostProcessor(
val timestamp = (item as? MatrixTimelineItem.Event)?.event?.timestamp ?: return false
return timestamp <= lastLoginTimestamp!!.time
}
-
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt
new file mode 100644
index 0000000000..712735649c
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.tracing
+
+/**
+ * This class is used to provide file, line, column information to the Rust SDK [org.matrix.rustcomponents.sdk.logEvent] method.
+ * The data is extracted from a [StackTraceElement] instance.
+ */
+data class LogEventLocation(
+ val file: String,
+ val line: UInt?,
+) {
+
+ companion object {
+ /**
+ * Create a [LogEventLocation] from a [StackTraceElement].
+ */
+ fun from(stackTraceElement: StackTraceElement): LogEventLocation {
+ return LogEventLocation(
+ file = stackTraceElement.fileName ?: "",
+ line = stackTraceElement.lineNumber.takeIf { it >= 0 }?.toUInt()
+ )
+ }
+ }
+}
+
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt
new file mode 100644
index 0000000000..c211f48c05
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.tracing
+
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.core.meta.BuildMeta
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
+import io.element.android.libraries.matrix.api.tracing.TracingService
+import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
+import org.matrix.rustcomponents.sdk.TracingFileConfiguration
+import timber.log.Timber
+import javax.inject.Inject
+
+@ContributesBinding(AppScope::class)
+class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService {
+
+ override fun setupTracing(tracingConfiguration: TracingConfiguration) {
+ val filter = tracingConfiguration.filterConfiguration
+ val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
+ filter = tracingConfiguration.filterConfiguration.filter,
+ writeToStdoutOrSystem = tracingConfiguration.writesToLogcat,
+ writeToFiles = when (val writeToFilesConfiguration = tracingConfiguration.writesToFilesConfiguration) {
+ is WriteToFilesConfiguration.Disabled -> null
+ is WriteToFilesConfiguration.Enabled -> TracingFileConfiguration(
+ path = writeToFilesConfiguration.directory,
+ filePrefix = writeToFilesConfiguration.filenamePrefix,
+ )
+ },
+ )
+ org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration)
+ Timber.v("Tracing config filter = $filter")
+ }
+
+ override fun createTimberTree(): Timber.Tree {
+ return RustTracingTree(retrieveFromStackTrace = buildMeta.isDebuggable)
+ }
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt
new file mode 100644
index 0000000000..275994081d
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.tracing
+
+import android.util.Log
+import io.element.android.libraries.matrix.api.tracing.Target
+import org.matrix.rustcomponents.sdk.LogLevel
+import org.matrix.rustcomponents.sdk.logEvent
+import timber.log.Timber
+
+/**
+ * List of fully qualified class names to ignore when looking for the first stack trace element.
+ */
+private val fqcnIgnore = listOf(
+ Timber::class.java.name,
+ Timber.Forest::class.java.name,
+ Timber.Tree::class.java.name,
+ RustTracingTree::class.java.name,
+)
+
+/**
+ * A Timber tree that passes logs to the Rust SDK.
+ */
+internal class RustTracingTree(private val retrieveFromStackTrace: Boolean) : Timber.Tree() {
+
+ override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
+ val location = if (retrieveFromStackTrace) {
+ getLogEventLocationFromStackTrace()
+ } else {
+ LogEventLocation("", null)
+ }
+ val logLevel = priority.toLogLevel()
+ logEvent(
+ file = location.file,
+ line = location.line,
+ level = logLevel,
+ target = Target.ELEMENT.filter,
+ message = message,
+ )
+ }
+
+ /**
+ * Extract the [LogEventLocation] from the stack trace.
+ */
+ private fun getLogEventLocationFromStackTrace(): LogEventLocation {
+ return Throwable(null, null).stackTrace
+ .first { it.className !in fqcnIgnore }
+ .let(LogEventLocation::from)
+ }
+}
+
+/**
+ * Convert a log priority to a Rust SDK log level.
+ */
+private fun Int.toLogLevel(): LogLevel {
+ return when (this) {
+ Log.VERBOSE -> LogLevel.TRACE
+ Log.DEBUG -> LogLevel.DEBUG
+ Log.INFO -> LogLevel.INFO
+ Log.WARN -> LogLevel.WARN
+ Log.ERROR -> LogLevel.ERROR
+ else -> LogLevel.DEBUG
+ }
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt
index a347973e89..fbf393e587 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt
@@ -21,11 +21,10 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import org.matrix.rustcomponents.sdk.TaskHandle
-internal fun mxCallbackFlow(block: suspend ProducerScope.() -> TaskHandle) =
+internal fun mxCallbackFlow(block: suspend ProducerScope.() -> TaskHandle?) =
callbackFlow {
- val token: TaskHandle = block(this)
+ val taskHandle: TaskHandle? = block(this)
awaitClose {
- token.cancel()
- token.destroy()
+ taskHandle?.cancelAndDestroy()
}
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt
new file mode 100644
index 0000000000..ac92a2e026
--- /dev/null
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.impl.util
+
+import org.matrix.rustcomponents.sdk.Disposable
+
+/**
+ * Call destroy on all elements of the iterable.
+ */
+internal fun Iterable.destroyAll() = forEach { it.destroy() }
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt
similarity index 73%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt
rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt
index 9a21645351..5842ba1546 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt
@@ -19,18 +19,22 @@ package io.element.android.libraries.matrix.impl.util
import org.matrix.rustcomponents.sdk.TaskHandle
import java.util.concurrent.CopyOnWriteArraySet
-class TaskHandleBag(private val tokens: MutableSet = CopyOnWriteArraySet()) : Set by tokens {
+fun TaskHandle.cancelAndDestroy() {
+ cancel()
+ destroy()
+}
+
+class TaskHandleBag(private val taskHandles: MutableSet = CopyOnWriteArraySet()) : Set by taskHandles {
operator fun plusAssign(taskHandle: TaskHandle?) {
if (taskHandle == null) return
- tokens += taskHandle
+ taskHandles += taskHandle
}
fun dispose() {
- tokens.forEach {
- it.cancel()
- it.destroy()
+ taskHandles.forEach {
+ it.cancelAndDestroy()
}
- tokens.clear()
+ taskHandles.clear()
}
}
diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt
index 91f0bc1883..63920cd6ce 100644
--- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt
+++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt
@@ -22,15 +22,18 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.util.Date
class TimelineEncryptedHistoryPostProcessorTest {
- private val defaultLastLoginTimestamp = Date(1689061264L)
+ private val defaultLastLoginTimestamp = Date(1_689_061_264L)
@Test
- fun `given an unencrypted room, nothing is done`() {
+ fun `given an unencrypted room, nothing is done`() = runTest {
val processor = createPostProcessor(isRoomEncrypted = false)
val items = listOf(
MatrixTimelineItem.Event(0L, anEventTimelineItem())
@@ -39,7 +42,7 @@ class TimelineEncryptedHistoryPostProcessorTest {
}
@Test
- fun `given a null lastLoginTimestamp, nothing is done`() {
+ fun `given a null lastLoginTimestamp, nothing is done`() = runTest {
val processor = createPostProcessor(lastLoginTimestamp = null)
val items = listOf(
MatrixTimelineItem.Event(0L, anEventTimelineItem())
@@ -48,14 +51,14 @@ class TimelineEncryptedHistoryPostProcessorTest {
}
@Test
- fun `given an empty list, nothing is done`() {
+ fun `given an empty list, nothing is done`() = runTest {
val processor = createPostProcessor()
val items = emptyList()
assertThat(processor.process(items)).isSameInstanceAs(items)
}
@Test
- fun `given a list with no items before lastLoginTimestamp, nothing is done`() {
+ fun `given a list with no items before lastLoginTimestamp, nothing is done`() = runTest {
val processor = createPostProcessor()
val items = listOf(
MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1))
@@ -64,7 +67,7 @@ class TimelineEncryptedHistoryPostProcessorTest {
}
@Test
- fun `given a list with an item with equal timestamp as lastLoginTimestamp, it's replaced`() {
+ fun `given a list with an item with equal timestamp as lastLoginTimestamp, it's replaced`() = runTest {
val processor = createPostProcessor()
val items = listOf(
MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time))
@@ -74,7 +77,7 @@ class TimelineEncryptedHistoryPostProcessorTest {
}
@Test
- fun `given a list with an item with a lower timestamp than lastLoginTimestamp, it's replaced`() {
+ fun `given a list with an item with a lower timestamp than lastLoginTimestamp, it's replaced`() = runTest {
val processor = createPostProcessor()
val items = listOf(
MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1))
@@ -85,7 +88,7 @@ class TimelineEncryptedHistoryPostProcessorTest {
}
@Test
- fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, they're replaced and the user can't back paginate`() {
+ fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, they're replaced and the user can't back paginate`() = runTest {
val paginationStateFlow = MutableStateFlow(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = true, isBackPaginating = false))
val processor = createPostProcessor(paginationStateFlow = paginationStateFlow)
val items = listOf(
@@ -102,7 +105,7 @@ class TimelineEncryptedHistoryPostProcessorTest {
assertThat(paginationStateFlow.value).isEqualTo(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = false, isBackPaginating = false))
}
- private fun createPostProcessor(
+ private fun TestScope.createPostProcessor(
lastLoginTimestamp: Date? = defaultLastLoginTimestamp,
isRoomEncrypted: Boolean = true,
paginationStateFlow: MutableStateFlow =
@@ -111,5 +114,6 @@ class TimelineEncryptedHistoryPostProcessorTest {
lastLoginTimestamp = lastLoginTimestamp,
isRoomEncrypted = isRoomEncrypted,
paginationStateFlow = paginationStateFlow,
+ dispatcher = StandardTestDispatcher(testScheduler)
)
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
index 1a654ac8d4..1229836e30 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt
@@ -27,7 +27,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@@ -35,7 +35,7 @@ import io.element.android.libraries.matrix.test.media.FakeMediaLoader
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
import io.element.android.libraries.matrix.test.pushers.FakePushersService
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
-import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
+import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import io.element.android.tests.testutils.simulateLongTask
@@ -45,7 +45,7 @@ class FakeMatrixClient(
override val sessionId: SessionId = A_SESSION_ID,
private val userDisplayName: Result = Result.success(A_USER_NAME),
private val userAvatarURLString: Result = Result.success(AN_AVATAR_URL),
- override val roomSummaryDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource(),
+ override val roomListService: RoomListService = FakeRoomListService(),
override val mediaLoader: MatrixMediaLoader = FakeMediaLoader(),
private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(),
private val pushersService: FakePushersService = FakePushersService(),
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt
index 3da47a8563..677774afe4 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt
@@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.core.SpaceId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UserId
-import java.util.UUID
const val A_USER_NAME = "alice"
const val A_PASSWORD = "password"
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt
index 9eb5a20ba4..7cb92d35c5 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt
@@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.notification.NotificationService
class FakeNotificationService : NotificationService {
- override fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result {
+ override suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result {
return Result.success(null)
}
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
index 7fe7de5b9f..ee735ae81b 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
@@ -56,6 +56,7 @@ class FakeMatrixRoom(
override val joinedMemberCount: Long = 123L,
override val activeMemberCount: Long = 234L,
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
+ canRedact: Boolean = false,
) : MatrixRoom {
private var ignoreResult: Result = Result.success(Unit)
@@ -66,6 +67,7 @@ class FakeMatrixRoom(
private var joinRoomResult = Result.success(Unit)
private var inviteUserResult = Result.success(Unit)
private var canInviteResult = Result.success(true)
+ private var canRedactResult = Result.success(canRedact)
private val canSendStateResults = mutableMapOf>()
private val canSendEventResults = mutableMapOf>()
private var sendMediaResult = Result.success(Unit)
@@ -100,7 +102,6 @@ class FakeMatrixRoom(
private val _sentLocations = mutableListOf()
val sentLocations: List = _sentLocations
-
var invitedUserId: UserId? = null
private set
@@ -128,9 +129,11 @@ class FakeMatrixRoom(
override val timeline: MatrixTimeline = matrixTimeline
- override fun open(): Result {
- return Result.success(Unit)
- }
+ override fun subscribeToSync() = Unit
+
+ override fun unsubscribeFromSync() = Unit
+
+ override fun destroy() = Unit
override suspend fun userDisplayName(userId: UserId): Result = simulateLongTask {
userDisplayNameResult
@@ -206,6 +209,10 @@ class FakeMatrixRoom(
return canInviteResult
}
+ override suspend fun canUserRedact(userId: UserId): Result {
+ return canRedactResult
+ }
+
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result {
return canSendStateResults[type] ?: Result.failure(IllegalStateException("No fake answer"))
}
@@ -283,8 +290,6 @@ class FakeMatrixRoom(
return sendLocationResult
}
- override fun close() = Unit
-
fun givenLeaveRoomError(throwable: Throwable?) {
this.leaveRoomError = throwable
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt
index 4df815c54c..59bac7ad40 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt
@@ -20,8 +20,8 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UserId
-import io.element.android.libraries.matrix.api.room.RoomSummary
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.matrix.api.room.message.RoomMessage
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt
similarity index 51%
rename from libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt
rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt
index cae36e14c8..fa2e347e3b 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-package io.element.android.libraries.matrix.test.room
+package io.element.android.libraries.matrix.test.roomlist
-import io.element.android.libraries.matrix.api.room.RoomSummary
-import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
+import io.element.android.libraries.matrix.api.roomlist.RoomList
+import io.element.android.libraries.matrix.api.roomlist.RoomListService
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-class FakeRoomSummaryDataSource : RoomSummaryDataSource {
+class FakeRoomListService : RoomListService {
private val allRoomSummariesFlow = MutableStateFlow>(emptyList())
private val inviteRoomSummariesFlow = MutableStateFlow>(emptyList())
- private val allRoomsLoadingStateFlow = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded)
+ private val allRoomsLoadingStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded)
+ private val inviteRoomsLoadingStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded)
+ private val roomListStateFlow = MutableStateFlow(RoomListService.State.Idle)
suspend fun postAllRooms(roomSummaries: List) {
allRoomSummariesFlow.emit(roomSummaries)
@@ -35,20 +38,16 @@ class FakeRoomSummaryDataSource : RoomSummaryDataSource {
inviteRoomSummariesFlow.emit(roomSummaries)
}
- suspend fun postLoadingState(loadingState: RoomSummaryDataSource.LoadingState) {
+ suspend fun postAllRoomsLoadingState(loadingState: RoomList.LoadingState) {
allRoomsLoadingStateFlow.emit(loadingState)
}
- override fun allRoomsLoadingState(): StateFlow {
- return allRoomsLoadingStateFlow
+ suspend fun postInviteRoomsLoadingState(loadingState: RoomList.LoadingState) {
+ inviteRoomsLoadingStateFlow.emit(loadingState)
}
- override fun allRooms(): StateFlow> {
- return allRoomSummariesFlow
- }
-
- override fun inviteRooms(): StateFlow> {
- return inviteRoomSummariesFlow
+ suspend fun postState(state: RoomListService.State) {
+ roomListStateFlow.emit(state)
}
var latestSlidingSyncRange: IntRange? = null
@@ -57,4 +56,20 @@ class FakeRoomSummaryDataSource : RoomSummaryDataSource {
override fun updateAllRoomsVisibleRange(range: IntRange) {
latestSlidingSyncRange = range
}
+
+ override fun allRooms(): RoomList {
+ return SimpleRoomList(
+ summaries = allRoomSummariesFlow,
+ loadingState = allRoomsLoadingStateFlow
+ )
+ }
+
+ override fun invites(): RoomList {
+ return SimpleRoomList(
+ summaries = inviteRoomSummariesFlow,
+ loadingState = inviteRoomsLoadingStateFlow
+ )
+ }
+
+ override val state: StateFlow = roomListStateFlow
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt
new file mode 100644
index 0000000000..28b04ae318
--- /dev/null
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.matrix.test.roomlist
+
+import io.element.android.libraries.matrix.api.roomlist.RoomList
+import io.element.android.libraries.matrix.api.roomlist.RoomSummary
+import kotlinx.coroutines.flow.StateFlow
+
+data class SimpleRoomList(
+ override val summaries: StateFlow>,
+ override val loadingState: StateFlow
+) : RoomList
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt
index dd653a76ec..4e618deb9a 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt
@@ -34,7 +34,7 @@ class FakeSyncService : SyncService {
return Result.success(Unit)
}
- override fun stopSync(): Result {
+ override suspend fun stopSync(): Result {
syncStateFlow.value = SyncState.Terminated
return Result.success(Unit)
}
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt
index b12f577c36..d541b534b1 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt
@@ -111,12 +111,12 @@ private fun AvatarActionBottomSheetContent(
@Preview
@Composable
-fun AvatarActionBottomSheetLightPreview() =
+internal fun AvatarActionBottomSheetLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
-fun AvatarActionBottomSheetDarkPreview() =
+internal fun AvatarActionBottomSheetDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt
index 6054aa53af..a42d44b642 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt
@@ -103,12 +103,12 @@ private fun MatrixUserHeaderContent(
@Preview
@Composable
-fun MatrixUserHeaderLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
+internal fun MatrixUserHeaderLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewLight { ContentToPreview(matrixUser) }
@Preview
@Composable
-fun MatrixUserHeaderDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
+internal fun MatrixUserHeaderDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewDark { ContentToPreview(matrixUser) }
@Composable
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt
index 57901edc03..faa80f82e6 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt
@@ -68,12 +68,12 @@ fun MatrixUserHeaderPlaceholder(
@Preview
@Composable
-fun MatrixUserHeaderPlaceholderLightPreview() =
+internal fun MatrixUserHeaderPlaceholderLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
-fun MatrixUserHeaderPlaceholderDarkPreview() =
+internal fun MatrixUserHeaderPlaceholderDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt
index 2881235335..da6f69d830 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt
@@ -46,7 +46,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
+import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt
index c0ed0460dc..f9649e1b34 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt
@@ -88,7 +88,7 @@ fun SelectedUsersList(
val maxVisibleUsers = rowWidth / userWidthWithSpacing
// Round down the number of visible users to end with a state where one is half visible
- val targetFraction = (userWidth / 2) / userWidthWithSpacing
+ val targetFraction = userWidth / 2 / userWidthWithSpacing
val targetUsers = floor(maxVisibleUsers - targetFraction) + targetFraction
// Work out how much extra spacing we need to reduce the number of users that much, then split it evenly amongst the visible users
@@ -153,7 +153,7 @@ private fun ContentToPreview() {
SelectedUsersList(
selectedUsers = aMatrixUserList().take(6).toImmutableList(),
modifier = Modifier
- .width((200 + (i * 20)).dp)
+ .width((200 + i * 20).dp)
.border(1.dp, Color.Red)
)
}
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt
index 2b5d2f6800..13fcb40792 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt
@@ -85,11 +85,11 @@ fun UnsavedAvatar(
@Preview
@Composable
-fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() }
+internal fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
-fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() }
+internal fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt
index 7995672e92..668b963bf1 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt
@@ -57,14 +57,9 @@ fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): St
val roomMembers = roomMembersState.roomMembers()
return remember(roomMembersState) {
derivedStateOf {
- if (roomMembers == null) {
- null
- } else if (roomMembers.size == 2 && isDirect && isEncrypted) {
- roomMembers.find { it.userId != this.sessionId }
- } else {
- null
- }
+ roomMembers
+ ?.takeIf { it.size == 2 && isDirect && isEncrypted }
+ ?.find { it.userId != sessionId }
}
}
}
-
diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt
index 005a0ac747..f2a73545bf 100644
--- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt
+++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MessageEventType
+import io.element.android.libraries.matrix.api.room.powerlevels.canRedact
import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage
@Composable
@@ -30,3 +31,10 @@ fun MatrixRoom.canSendMessageAsState(type: MessageEventType, updateKey: Long): S
}
}
+@Composable
+fun MatrixRoom.canRedactAsState(updateKey: Long): State {
+ return produceState(initialValue = false, key1 = updateKey) {
+ value = canRedact().getOrElse { false }
+ }
+}
+
diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt
index cfa59d65d3..1622ab2eef 100644
--- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt
+++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt
@@ -82,7 +82,6 @@ class MediaSender @Inject constructor(
progressCallback = progressCallback
)
}
- else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $uploadInfo"))
}
}
}
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt
index 2b8669fe42..938072433a 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt
@@ -19,7 +19,6 @@ package io.element.android.libraries.mediaupload
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
-import com.vanniktech.blurhash.BlurHash
import io.element.android.libraries.androidutils.bitmap.calculateInSampleSize
import io.element.android.libraries.androidutils.bitmap.resizeToMax
import io.element.android.libraries.androidutils.bitmap.rotateToMetadataOrientation
@@ -92,7 +91,7 @@ class ImageCompressor @Inject constructor(
) {
val (width, height) = when (resizeMode) {
is ResizeMode.Approximate -> resizeMode.desiredWidth to resizeMode.desiredHeight
- is ResizeMode.Strict -> (resizeMode.maxWidth / 2) to (resizeMode.maxHeight / 2)
+ is ResizeMode.Strict -> resizeMode.maxWidth / 2 to resizeMode.maxHeight / 2
is ResizeMode.None -> return
}
// Read bounds only
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt
index a9ed6319cb..2cee2566d1 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt
@@ -69,6 +69,7 @@ class ThumbnailFactory @Inject constructor(
cancellationSignal
)
} else {
+ @Suppress("DEPRECATION")
ThumbnailUtils.createImageThumbnail(
file.path,
MediaStore.Images.Thumbnails.MINI_KIND,
diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
index 30f19aa31c..d9dba2a0d3 100644
--- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
+++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
@@ -83,12 +83,12 @@ fun PermissionsView(
@Preview
@Composable
-fun PermissionsViewLightPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) =
+internal fun PermissionsViewLightPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
-fun PermissionsViewDarkPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) =
+internal fun PermissionsViewDarkPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
diff --git a/libraries/permissions/impl/build.gradle.kts b/libraries/permissions/impl/build.gradle.kts
index 31a4e4bbb1..9808986f8f 100644
--- a/libraries/permissions/impl/build.gradle.kts
+++ b/libraries/permissions/impl/build.gradle.kts
@@ -57,7 +57,5 @@ dependencies {
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
- androidTestImplementation(libs.test.junitext)
-
ksp(libs.showkase.processor)
}
diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
index 24b174426c..f501bcee8a 100644
--- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
+++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
@@ -18,7 +18,7 @@
package io.element.android.libraries.permissions.impl
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -41,7 +41,7 @@ class DefaultPermissionsPresenterTest {
permissionsStore,
permissionStateProvider
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -64,7 +64,7 @@ class DefaultPermissionsPresenterTest {
permissionsStore,
permissionStateProvider
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -84,7 +84,7 @@ class DefaultPermissionsPresenterTest {
permissionsStore,
permissionStateProvider
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -113,7 +113,7 @@ class DefaultPermissionsPresenterTest {
permissionsStore,
permissionStateProvider
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
@@ -142,7 +142,7 @@ class DefaultPermissionsPresenterTest {
permissionsStore,
permissionStateProvider
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
@@ -164,7 +164,7 @@ class DefaultPermissionsPresenterTest {
permissionsStore,
permissionStateProvider
)
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt
index 9992480436..912edd9294 100644
--- a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt
+++ b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt
@@ -16,7 +16,7 @@
package io.element.android.libraries.permissions.noop
-import app.cash.molecule.RecompositionClock
+import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
@@ -27,7 +27,7 @@ class NoopPermissionsPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = NoopPermissionsPresenter()
- moleculeFlow(RecompositionClock.Immediate) {
+ moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts
index b639f61b8b..b0f3be6deb 100644
--- a/libraries/push/impl/build.gradle.kts
+++ b/libraries/push/impl/build.gradle.kts
@@ -52,9 +52,6 @@ dependencies {
implementation(projects.services.appnavstate.api)
implementation(projects.services.toolbox.api)
- // TODO Temporary use the deprecated LocalBroadcastManager, to be changed later.
- implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0")
-
testImplementation(libs.test.junit)
testImplementation(libs.test.mockk)
testImplementation(libs.test.truth)
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
index 81a86c5345..f3afb940cd 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
@@ -103,33 +103,6 @@ class PushersManager @Inject constructor(
return "{\"cs\":\"$secretForUser\"}"
}
- suspend fun registerEmailForPush(email: String) {
- TODO()
- /*
- val currentSession = activeSessionHolder.getActiveSession()
- val appName = appNameProvider.getAppName()
- currentSession.pushersService().addEmailPusher(
- email = email,
- lang = localeProvider.current().language,
- emailBranding = appName,
- appDisplayName = appName,
- deviceDisplayName = currentSession.sessionParams.deviceId ?: "MOBILE"
- )
- */
- }
-
- fun getPusherForCurrentSession() {}/*: Pusher? {
- val session = activeSessionHolder.getSafeActiveSession() ?: return null
- val deviceId = session.sessionParams.deviceId
- return session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
- }
- */
-
- suspend fun unregisterEmailPusher(email: String) {
- // val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
- // currentSession.pushersService().removeEmailPusher(email)
- }
-
override suspend fun unregisterPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
matrixClient.pushersService().unsetHttpPusher()
}
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt
index 9cd4956dca..91b3987251 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt
@@ -23,17 +23,17 @@ import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
+import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.user.MatrixUser
-import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
-import io.element.android.libraries.push.api.store.PushDataStore
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
-import io.element.android.services.appnavstate.api.NavigationState
import io.element.android.services.appnavstate.api.AppNavigationStateService
+import io.element.android.services.appnavstate.api.NavigationState
+import io.element.android.services.appnavstate.api.currentSessionId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -48,7 +48,6 @@ import javax.inject.Inject
*/
@SingleIn(AppScope::class)
class DefaultNotificationDrawerManager @Inject constructor(
- private val pushDataStore: PushDataStore,
private val notifiableEventProcessor: NotifiableEventProcessor,
private val notificationRenderer: NotificationRenderer,
private val notificationEventPersistence: NotificationEventPersistence,
@@ -76,9 +75,16 @@ class DefaultNotificationDrawerManager @Inject constructor(
}
}
+ private var currentAppNavigationState: NavigationState? = null
+
private fun onAppNavigationStateChange(navigationState: NavigationState) {
when (navigationState) {
- NavigationState.Root -> {}
+ NavigationState.Root -> {
+ currentAppNavigationState?.currentSessionId()?.let { sessionId ->
+ // User signed out, clear all notifications related to the session.
+ clearAllEvents(sessionId)
+ }
+ }
is NavigationState.Session -> {}
is NavigationState.Space -> {}
is NavigationState.Room -> {
@@ -93,6 +99,7 @@ class DefaultNotificationDrawerManager @Inject constructor(
)
}
}
+ currentAppNavigationState = navigationState
}
private fun createInitialNotificationState(): NotificationState {
@@ -133,12 +140,21 @@ class DefaultNotificationDrawerManager @Inject constructor(
/**
* Clear all known events and refresh the notification drawer.
*/
- fun clearAllEvents(sessionId: SessionId) {
+ fun clearAllMessagesEvents(sessionId: SessionId) {
updateEvents {
it.clearMessagesForSession(sessionId)
}
}
+ /**
+ * Clear all notifications related to the session and refresh the notification drawer.
+ */
+ fun clearAllEvents(sessionId: SessionId) {
+ updateEvents {
+ it.clearAllForSession(sessionId)
+ }
+ }
+
/**
* Should be called when the application is currently opened and showing timeline for the given roomId.
* Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room.
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt
index a24f088998..2d4d27472b 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt
@@ -27,7 +27,7 @@ class FilteredEventDetector @Inject constructor(
* Returns true if the given event should be ignored.
* Used to skip notifications if a non expected message is received.
*/
- fun shouldBeIgnored(notifiableEvent: NotifiableEvent): Boolean {
+ fun shouldBeIgnored(@Suppress("UNUSED_PARAMETER") notifiableEvent: NotifiableEvent): Boolean {
/* TODO EAx
val session = activeSessionDataSource.currentValue?.orNull() ?: return false
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
index dbdddf1ac9..e5af7785db 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
@@ -17,11 +17,12 @@
package io.element.android.libraries.push.impl.notifications
import io.element.android.libraries.core.log.logger.LoggerTag
-import io.element.android.libraries.core.meta.BuildMeta
+import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
+import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.room.RoomMembershipState
@@ -34,7 +35,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
-import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
@@ -57,9 +57,6 @@ private val loggerTag = LoggerTag("NotifiableEventResolver", pushLoggerTag)
*/
class NotifiableEventResolver @Inject constructor(
private val stringProvider: StringProvider,
- // private val noticeEventFormatter: NoticeEventFormatter,
- // private val displayableEventFormatter: DisplayableEventFormatter,
- private val buildMeta: BuildMeta,
private val clock: SystemClock,
private val matrixClientProvider: MatrixClientProvider,
) {
@@ -72,31 +69,27 @@ class NotifiableEventResolver @Inject constructor(
userId = sessionId,
roomId = roomId,
eventId = eventId,
- // FIXME should be true in the future, but right now it's broken
- // (https://github.com/vector-im/element-x-android/issues/640#issuecomment-1612913658)
- filterByPushRules = false,
).onFailure {
Timber.tag(loggerTag.value).e(it, "Unable to resolve event: $eventId.")
}.getOrNull()
// TODO this notificationData is not always valid at the moment, sometimes the Rust SDK can't fetch the matching event
return notificationData?.asNotifiableEvent(sessionId)
- ?: fallbackNotifiableEvent(sessionId, roomId, eventId)
}
private fun NotificationData.asNotifiableEvent(userId: SessionId): NotifiableEvent? {
- return when (val content = this.event.content) {
+ return when (val content = this.content) {
is NotificationContent.MessageLike.RoomMessage -> {
buildNotifiableMessageEvent(
sessionId = userId,
+ senderId = content.senderId,
roomId = roomId,
eventId = eventId,
noisy = isNoisy,
- timestamp = event.timestamp,
+ timestamp = this.timestamp,
senderName = senderDisplayName,
- senderId = senderId.value,
body = descriptionFromMessageContent(content),
- imageUriString = event.contentUrl,
+ imageUriString = this.contentUrl,
roomName = roomDisplayName,
roomIsDirect = isDirect,
roomAvatarPath = roomAvatarUrl,
@@ -113,7 +106,7 @@ class NotifiableEventResolver @Inject constructor(
canBeReplaced = true,
roomName = roomDisplayName,
noisy = isNoisy,
- timestamp = event.timestamp,
+ timestamp = this.timestamp,
soundName = null,
isRedacted = false,
isUpdated = false,
@@ -122,10 +115,10 @@ class NotifiableEventResolver @Inject constructor(
title = null, // TODO check if title is needed anymore
)
} else {
- null
+ fallbackNotifiableEvent(userId, roomId, eventId)
}
}
- else -> null
+ else -> fallbackNotifiableEvent(userId, roomId, eventId)
}
}
@@ -181,6 +174,7 @@ class NotifiableEventResolver @Inject constructor(
@Suppress("LongParameterList")
private fun buildNotifiableMessageEvent(
sessionId: SessionId,
+ senderId: UserId,
roomId: RoomId,
eventId: EventId,
editedEventId: EventId? = null,
@@ -188,7 +182,6 @@ private fun buildNotifiableMessageEvent(
noisy: Boolean,
timestamp: Long,
senderName: String?,
- senderId: String?,
body: String?,
// We cannot use Uri? type here, as that could trigger a
// NotSerializableException when persisting this to storage
@@ -206,6 +199,7 @@ private fun buildNotifiableMessageEvent(
isUpdated: Boolean = false
) = NotifiableMessageEvent(
sessionId = sessionId,
+ senderId = senderId,
roomId = roomId,
eventId = eventId,
editedEventId = editedEventId,
@@ -213,7 +207,6 @@ private fun buildNotifiableMessageEvent(
noisy = noisy,
timestamp = timestamp,
senderName = senderName,
- senderId = senderId,
body = body,
imageUriString = imageUriString,
threadId = threadId,
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt
index d5df1001ca..59a763bd55 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt
@@ -19,15 +19,12 @@ package io.element.android.libraries.push.impl.notifications
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import androidx.core.app.RemoteInput
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
-import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.push.impl.log.notificationLoggerTag
-import io.element.android.services.toolbox.api.systemclock.SystemClock
import timber.log.Timber
import javax.inject.Inject
@@ -39,10 +36,6 @@ private val loggerTag = LoggerTag("NotificationBroadcastReceiver", notificationL
class NotificationBroadcastReceiver : BroadcastReceiver() {
@Inject lateinit var defaultNotificationDrawerManager: DefaultNotificationDrawerManager
-
- //@Inject lateinit var activeSessionHolder: ActiveSessionHolder
- //@Inject lateinit var analyticsTracker: AnalyticsTracker
- @Inject lateinit var clock: SystemClock
@Inject lateinit var actionIds: NotificationActionIds
override fun onReceive(context: Context?, intent: Intent?) {
@@ -59,7 +52,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
defaultNotificationDrawerManager.clearMessagesForRoom(sessionId, roomId)
}
actionIds.dismissSummary ->
- defaultNotificationDrawerManager.clearAllEvents(sessionId)
+ defaultNotificationDrawerManager.clearAllMessagesEvents(sessionId)
actionIds.dismissInvite -> if (roomId != null) {
defaultNotificationDrawerManager.clearMembershipNotificationForRoom(sessionId, roomId)
}
@@ -81,6 +74,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
}
}
+ @Suppress("UNUSED_PARAMETER")
private fun handleJoinRoom(sessionId: SessionId, roomId: RoomId) {
/*
activeSessionHolder.getSafeActiveSession()?.let { session ->
@@ -94,10 +88,10 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
}
}
}
-
*/
}
+ @Suppress("UNUSED_PARAMETER")
private fun handleRejectRoom(sessionId: SessionId, roomId: RoomId) {
/*
activeSessionHolder.getSafeActiveSession()?.let { session ->
@@ -109,6 +103,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
*/
}
+ @Suppress("UNUSED_PARAMETER")
private fun handleMarkAsRead(sessionId: SessionId, roomId: RoomId) {
/*
activeSessionHolder.getActiveSession().let { session ->
@@ -123,7 +118,9 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
*/
}
+ @Suppress("UNUSED_PARAMETER")
private fun handleSmartReply(intent: Intent, context: Context) {
+ /*
val message = getReplyMessage(intent)
val sessionId = intent.getStringExtra(KEY_SESSION_ID)?.let(::SessionId)
val roomId = intent.getStringExtra(KEY_ROOM_ID)?.let(::RoomId)
@@ -134,13 +131,11 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
// Can this happen? should we update notification?
return
}
- /*
activeSessionHolder.getActiveSession().let { session ->
session.getRoom(roomId)?.let { room ->
sendMatrixEvent(message, threadId, session, room, context)
}
}
-
*/
}
@@ -234,6 +229,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
*/
+ /*
private fun getReplyMessage(intent: Intent?): String? {
if (intent != null) {
val remoteInput = RemoteInput.getResultsFromIntent(intent)
@@ -243,6 +239,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
}
return null
}
+ */
companion object {
const val KEY_SESSION_ID = "sessionID"
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt
index 97b90476b0..6b6730c904 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt
@@ -154,6 +154,11 @@ data class NotificationEventQueue constructor(
queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId }
}
+ fun clearAllForSession(sessionId: SessionId) {
+ Timber.d("clearAllForSession $sessionId")
+ queue.removeAll { it.sessionId == sessionId }
+ }
+
fun clearMessagesForRoom(sessionId: SessionId, roomId: RoomId) {
Timber.d("clearMessageEventOfRoom $sessionId, $roomId")
queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId && it.roomId == roomId }
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt
index 5b15dc78d2..27713399fc 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt
@@ -28,7 +28,7 @@ class OutdatedEventDetector @Inject constructor(
* Used to clean up notifications if a displayed message has been read on an
* other device.
*/
- fun isMessageOutdated(notifiableEvent: NotifiableEvent): Boolean {
+ fun isMessageOutdated(@Suppress("UNUSED_PARAMETER") notifiableEvent: NotifiableEvent): Boolean {
/* TODO EAx
val session = activeSessionDataSource.currentValue?.orNull() ?: return false
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt
index 734c34b051..96a8b90f06 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt
@@ -23,17 +23,15 @@ import io.element.android.libraries.matrix.api.core.SessionId
* Data class to hold information about a group of notifications for a room.
*/
data class RoomEventGroupInfo(
- val sessionId: SessionId,
- val roomId: RoomId,
- val roomDisplayName: String,
- val isDirect: Boolean = false
-) {
+ val sessionId: SessionId,
+ val roomId: RoomId,
+ val roomDisplayName: String,
+ val isDirect: Boolean = false,
// An event in the list has not yet been display
- var hasNewEvent: Boolean = false
-
+ val hasNewEvent: Boolean = false,
// true if at least one on the not yet displayed event is noisy
- var shouldBing: Boolean = false
- var customSound: String? = null
- var hasSmartReplyError: Boolean = false
- var isUpdated: Boolean = false
-}
+ val shouldBing: Boolean = false,
+ val customSound: String? = null,
+ val hasSmartReplyError: Boolean = false,
+ val isUpdated: Boolean = false,
+)
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
index 5f2f6db263..29d828d34c 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
@@ -69,7 +69,7 @@ class RoomGroupMessageCreator @Inject constructor(
val lastMessageTimestamp = events.last().timestamp
val smartReplyErrors = events.filter { it.isSmartReplyError() }
- val messageCount = (events.size - smartReplyErrors.size)
+ val messageCount = events.size - smartReplyErrors.size
val meta = RoomNotification.Message.Meta(
summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, roomIsDirect = !roomIsGroup),
messageCount = messageCount,
@@ -85,12 +85,11 @@ class RoomGroupMessageCreator @Inject constructor(
roomId = roomId,
roomDisplayName = roomName,
isDirect = !roomIsGroup,
- ).also {
- it.hasSmartReplyError = smartReplyErrors.isNotEmpty()
- it.shouldBing = meta.shouldBing
- it.customSound = events.last().soundName
- it.isUpdated = events.last().isUpdated
- },
+ hasSmartReplyError = smartReplyErrors.isNotEmpty(),
+ shouldBing = meta.shouldBing,
+ customSound = events.last().soundName,
+ isUpdated = events.last().isUpdated,
+ ),
threadId = lastKnownRoomEvent.threadId,
largeIcon = largeBitmap,
lastMessageTimestamp,
@@ -108,7 +107,7 @@ class RoomGroupMessageCreator @Inject constructor(
Person.Builder()
.setName(event.senderName?.annotateForDebug(70))
.setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath))
- .setKey(event.senderId)
+ .setKey(event.senderId.value)
.build()
}
when {
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt
index 42c0fe61af..a7135cee74 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt
@@ -19,12 +19,10 @@ package io.element.android.libraries.push.impl.notifications
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
class TestNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- // Internal broadcast to any one interested
- LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
+ // TODO The test notification has been clicked, notify the ui
}
}
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt
index 0624b863ed..ab115262de 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt
@@ -162,7 +162,7 @@ class NotificationChannels @Inject constructor(
private const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V2"
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
- private fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ private fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
fun openSystemSettingsForSilentCategory(activity: Activity) {
activity.startNotificationChannelSettingsIntent(SILENT_NOTIFICATION_CHANNEL_ID)
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
index 57a3eb45aa..1b6bb8a67a 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
@@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
+import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
import io.element.android.services.appnavstate.api.AppNavigationState
import io.element.android.services.appnavstate.api.currentRoomId
@@ -32,10 +33,10 @@ data class NotifiableMessageEvent(
override val eventId: EventId,
override val editedEventId: EventId?,
override val canBeReplaced: Boolean,
+ val senderId: UserId,
val noisy: Boolean,
val timestamp: Long,
val senderName: String?,
- val senderId: String?,
val body: String?,
// We cannot use Uri? type here, as that could trigger a
// NotSerializableException when persisting this to storage
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt
index f252765530..4b262983d4 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt
@@ -30,7 +30,7 @@ data class SimpleNotifiableEvent(
val type: String?,
val timestamp: Long,
val soundName: String?,
- override var canBeReplaced: Boolean,
+ override val canBeReplaced: Boolean,
override val isRedacted: Boolean = false,
override val isUpdated: Boolean = false
) : NotifiableEvent
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
index 3ad848aeb4..c3d68e52ac 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
@@ -16,22 +16,17 @@
package io.element.android.libraries.push.impl.push
-import android.content.Context
-import android.content.Intent
import android.os.Handler
import android.os.Looper
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
-import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.log.pushLoggerTag
-import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
-import io.element.android.libraries.push.impl.notifications.NotificationActionIds
import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager
+import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
import io.element.android.libraries.push.impl.store.DefaultPushDataStore
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.libraries.pushproviders.api.PushHandler
@@ -53,8 +48,7 @@ class DefaultPushHandler @Inject constructor(
private val defaultPushDataStore: DefaultPushDataStore,
private val userPushStoreFactory: UserPushStoreFactory,
private val pushClientSecret: PushClientSecret,
- private val actionIds: NotificationActionIds,
- @ApplicationContext private val context: Context,
+ // private val actionIds: NotificationActionIds,
private val buildMeta: BuildMeta,
private val matrixAuthenticationService: MatrixAuthenticationService,
) : PushHandler {
@@ -82,8 +76,8 @@ class DefaultPushHandler @Inject constructor(
// Diagnostic Push
if (pushData.eventId == PushersManager.TEST_EVENT_ID) {
- val intent = Intent(actionIds.push)
- LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
+ // val intent = Intent(actionIds.push)
+ // TODO The test push has been received, notify the ui
return
}
@@ -106,15 +100,15 @@ class DefaultPushHandler @Inject constructor(
}
val clientSecret = pushData.clientSecret
- val userId = if (clientSecret == null) {
- // Should not happen. In this case, restore default session
- null
- } else {
- // Get userId from client secret
- pushClientSecret.getUserIdFromSecret(clientSecret)
- } ?: run {
- matrixAuthenticationService.getLatestSessionId()
- }
+ // clientSecret should not be null. If this happens, restore default session
+ val userId = clientSecret
+ ?.let {
+ // Get userId from client secret
+ pushClientSecret.getUserIdFromSecret(clientSecret)
+ }
+ ?: run {
+ matrixAuthenticationService.getLatestSessionId()
+ }
if (userId == null) {
Timber.w("Unable to get a session")
diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..697a0f01d8
--- /dev/null
+++ b/libraries/push/impl/src/main/res/values-ru/translations.xml
@@ -0,0 +1,58 @@
+
+
+ "Позвонить"
+ "Прослушивание событий"
+ "Шумные уведомления"
+ "Бесшумные уведомления"
+ "** Не удалось отправить - пожалуйста, откройте комнату"
+ "Присоединиться"
+ "Отклонить"
+ "Пригласил вас в чат"
+ "Новые сообщения"
+ "Отреагировал на %1$s"
+ "Отметить как прочитанное"
+ "Пригласил вас в комнату"
+ "Я"
+ "Вы просматриваете уведомление! Нажмите на меня!"
+ "%1$s: %2$s"
+ "%1$s: %2$s %3$s"
+ "%1$s и %2$s"
+ "%1$s в %2$s"
+ "%1$s в %2$s и %3$s"
+
+ - "%1$s: %2$d сообщение"
+ - "%1$s: %2$d сообщения"
+ - "%1$s: %2$d сообщений"
+
+
+ - "%d уведомление"
+ - "%d уведомления"
+ - "%d уведомлений"
+
+
+ - "%d приглашение"
+ - "%d приглашения"
+ - "%d приглашений"
+
+
+ - "%d новое сообщение"
+ - "%d новых сообщения"
+ - "%d новых сообщений"
+
+
+ - "%d непрочитанное уведомление"
+ - "%d непрочитанных уведомления"
+ - "%d непрочитанных уведомлений"
+
+
+ - "%d комната"
+ - "%d комнаты"
+ - "%d комнат"
+
+ "Выберите способ получения уведомлений"
+ "Фоновая синхронизация"
+ "Сервисы Google"
+ "Не найдены действующие службы Google Play. Уведомления могут работать некорректно."
+ "Уведомление"
+ "Быстрый ответ"
+
diff --git a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..248fae8b0b
--- /dev/null
+++ b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,33 @@
+
+
+ "通話"
+ "無聲通知"
+ "加入"
+ "拒絕"
+ "邀請您聊天"
+ "新訊息"
+ "標示為已讀"
+ "邀請您加入聊天室"
+ "我"
+ "您正在查看通知!點我!"
+
+ - "%1$s:%2$d 則訊息"
+
+
+ - "%d 個通知"
+
+
+ - "%d 個邀請"
+
+
+ - "%d 則新訊息"
+
+
+ - "%d 個聊天室"
+
+ "選擇接收通知的機制"
+ "背景同步"
+ "Google 服務"
+ "通知"
+ "快速回覆"
+
diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt
index 57f28e72db..b9664ef577 100644
--- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt
+++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt
@@ -25,7 +25,7 @@ class NotificationIdProviderTest {
@Test
fun `test notification id provider`() {
val sut = NotificationIdProvider()
- val offsetForASessionId = 305410
+ val offsetForASessionId = 305_410
assertThat(sut.getSummaryNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 0)
assertThat(sut.getRoomMessagesNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 1)
assertThat(sut.getRoomEventNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 2)
diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
index 9a998abf43..780d2abb71 100644
--- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
+++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
@@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
+import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
@@ -84,7 +85,7 @@ fun aNotifiableMessageEvent(
noisy = false,
timestamp = 0,
senderName = "sender-name",
- senderId = "sending-id",
+ senderId = UserId("@sending-id:domain.com"),
body = "message-body",
roomId = roomId,
threadId = threadId,
diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt
deleted file mode 100644
index d557aa0334..0000000000
--- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2023 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.
- */
-
-package io.element.android.libraries.pushproviders.firebase
-
-import javax.inject.Inject
-
-// TODO
-class EnsureFcmTokenIsRetrievedUseCase @Inject constructor(
-// private val unifiedPushHelper: UnifiedPushHelper,
-// private val fcmHelper: FcmHelper,
- // private val activeSessionHolder: ActiveSessionHolder,
-) {
-
-// fun execute(pushersManager: PushersManager, registerPusher: Boolean) {
-// if (unifiedPushHelper.isEmbeddedDistributor()) {
-// fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher))
-// }
-// }
-
- private fun shouldAddHttpPusher(registerPusher: Boolean) = if (registerPusher) {
- /*
- TODO EAx
- val currentSession = activeSessionHolder.getActiveSession()
- val currentPushers = currentSession.pushersService().getPushers()
- currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId }
- */
- true
- } else {
- false
- }
-}
diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt
index 9dedf9648f..795c8bb1e8 100644
--- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt
+++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt
@@ -36,7 +36,7 @@ import io.element.android.libraries.pushproviders.api.PushData
data class PushDataFirebase(
val eventId: String?,
val roomId: String?,
- var unread: Int?,
+ val unread: Int?,
val clientSecret: String?
)
diff --git a/libraries/pushproviders/firebase/src/main/res/values/firebase.xml b/libraries/pushproviders/firebase/src/main/res/values/firebase.xml
index 163717db91..b73238c79d 100644
--- a/libraries/pushproviders/firebase/src/main/res/values/firebase.xml
+++ b/libraries/pushproviders/firebase/src/main/res/values/firebase.xml
@@ -1,10 +1,10 @@
-
+
912726360885-e87n3jva9uoj4vbidvijq78ebg02asv2.apps.googleusercontent.com
https://vector-alpha.firebaseio.com
912726360885
AIzaSyAFZX8IhIfgzdOZvxDP_ISO5WYoU7jmQ5c
AIzaSyAFZX8IhIfgzdOZvxDP_ISO5WYoU7jmQ5c
vector-alpha.appspot.com
- vector-alpha
+ vector-alpha
diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt
index f092d0167c..4485cb2c7f 100644
--- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt
+++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt
@@ -47,7 +47,7 @@ data class PushDataUnifiedPush(
data class PushDataUnifiedPushNotification(
@SerialName("event_id") val eventId: String? = null,
@SerialName("room_id") val roomId: String? = null,
- @SerialName("counts") var counts: PushDataUnifiedPushCounts? = null,
+ @SerialName("counts") val counts: PushDataUnifiedPushCounts? = null,
)
@Serializable
diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt
index 4efaacbf3a..4bf8217914 100644
--- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt
+++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt
@@ -26,7 +26,7 @@ class UnregisterUnifiedPushUseCase @Inject constructor(
@ApplicationContext private val context: Context,
//private val pushDataStore: PushDataStore,
private val unifiedPushStore: UnifiedPushStore,
- private val unifiedPushGatewayResolver: UnifiedPushGatewayResolver,
+ // private val unifiedPushGatewayResolver: UnifiedPushGatewayResolver,
) {
suspend fun execute(clientSecret: String /*pushersManager: PushersManager?*/) {
diff --git a/libraries/pushstore/impl/build.gradle.kts b/libraries/pushstore/impl/build.gradle.kts
index dca5c82a4d..5946e77694 100644
--- a/libraries/pushstore/impl/build.gradle.kts
+++ b/libraries/pushstore/impl/build.gradle.kts
@@ -20,6 +20,11 @@ plugins {
android {
namespace = "io.element.android.libraries.push.pushstore.impl"
+
+ defaultConfig {
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunnerArguments["clearPackageData"] = "true"
+ }
}
anvil {
@@ -43,4 +48,13 @@ dependencies {
testImplementation(libs.coroutines.test)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.services.appnavstate.test)
+
+ androidTestImplementation(libs.coroutines.test)
+ androidTestImplementation(libs.test.core)
+ androidTestImplementation(libs.test.junit)
+ androidTestImplementation(libs.test.truth)
+ androidTestImplementation(libs.test.runner)
+ androidTestImplementation(projects.libraries.sessionStorage.test)
+
+ coreLibraryDesugaring(libs.android.desugar)
}
diff --git a/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt
new file mode 100644
index 0000000000..c87c772ddf
--- /dev/null
+++ b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.pushstore.impl
+
+import androidx.test.platform.app.InstrumentationRegistry
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.pushstore.api.UserPushStore
+import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import kotlin.concurrent.thread
+
+/**
+ * Note: to clear the emulator, invoke:
+ * adb uninstall io.element.android.libraries.push.pushstore.impl.test
+ */
+class DefaultUserPushStoreFactoryTest {
+
+ /**
+ * Ensure that creating UserPushStore is thread safe.
+ */
+ @Test
+ fun testParallelCreation() {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
+ val sessionId = SessionId("@alice:server.org")
+ val userPushStoreFactory = DefaultUserPushStoreFactory(context, NoOpSessionObserver())
+ var userPushStore1: UserPushStore? = null
+ val thread1 = thread {
+ userPushStore1 = userPushStoreFactory.create(sessionId)
+ }
+ var userPushStore2: UserPushStore? = null
+ val thread2 = thread {
+ userPushStore2 = userPushStoreFactory.create(sessionId)
+ }
+ thread1.join()
+ thread2.join()
+ runBlocking {
+ userPushStore1!!.areNotificationEnabledForDevice()
+ userPushStore2!!.areNotificationEnabledForDevice()
+ }
+ }
+}
diff --git a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt
index a84fe2ea69..8c85dca80c 100644
--- a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt
+++ b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt
@@ -26,6 +26,7 @@ import io.element.android.libraries.pushstore.api.UserPushStore
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@SingleIn(AppScope::class)
@@ -39,7 +40,7 @@ class DefaultUserPushStoreFactory @Inject constructor(
}
// We can have only one class accessing a single data store, so keep a cache of them.
- private val cache = mutableMapOf()
+ private val cache = ConcurrentHashMap()
override fun create(userId: SessionId): UserPushStore {
return cache.getOrPut(userId) {
UserPushStoreDataStore(
diff --git a/libraries/session-storage/test/build.gradle.kts b/libraries/session-storage/test/build.gradle.kts
new file mode 100644
index 0000000000..0c8de84669
--- /dev/null
+++ b/libraries/session-storage/test/build.gradle.kts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.libraries.sessionstorage.test"
+}
+
+dependencies {
+ implementation(projects.libraries.sessionStorage.api)
+}
diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt
new file mode 100644
index 0000000000..03487a3701
--- /dev/null
+++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.libraries.sessionstorage.test.observer
+
+import io.element.android.libraries.sessionstorage.api.observer.SessionListener
+import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
+
+class NoOpSessionObserver : SessionObserver {
+ override fun addListener(listener: SessionListener) = Unit
+ override fun removeListener(listener: SessionListener) = Unit
+}
diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
index 8c4c361707..d407924323 100644
--- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
+++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
@@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.ripple.rememberRipple
@@ -65,6 +66,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -73,6 +75,7 @@ import io.element.android.libraries.designsystem.VectorIcons
import io.element.android.libraries.designsystem.modifiers.applyIf
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.text.applyScaleUp
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
@@ -109,12 +112,15 @@ fun TextComposer(
) {
AttachmentButton(onClick = onAddAttachment, modifier = Modifier.padding(vertical = 6.dp))
Spacer(modifier = Modifier.width(12.dp))
+ val roundCornerSmall = 20.dp.applyScaleUp()
+ val roundCornerLarge = 28.dp.applyScaleUp()
+
var lineCount by remember { mutableStateOf(0) }
val roundedCornerSize = remember(lineCount, composerMode) {
if (lineCount > 1 || composerMode is MessageComposerMode.Special) {
- 20.dp
+ roundCornerSmall
} else {
- 28.dp
+ roundCornerLarge
}
}
val roundedCornerSizeState = animateDpAsState(
@@ -124,7 +130,7 @@ fun TextComposer(
)
)
val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value)
- val minHeight = 42.dp
+ val minHeight = 42.dp.applyScaleUp()
val bgColor = ElementTheme.colors.bgSubtleSecondary
// Change border color depending on focus
var hasFocus by remember { mutableStateOf(false) }
@@ -155,6 +161,9 @@ fun TextComposer(
onTextLayout = {
lineCount = it.lineCount
},
+ keyboardOptions = KeyboardOptions(
+ capitalization = KeyboardCapitalization.Sentences,
+ ),
textStyle = defaultTypography.copy(color = MaterialTheme.colorScheme.primary),
cursorBrush = SolidColor(ElementTheme.colors.iconAccentTertiary),
decorationBox = { innerTextField ->
@@ -165,7 +174,12 @@ fun TextComposer(
singleLine = false,
visualTransformation = VisualTransformation.None,
shape = roundedCorners,
- contentPadding = PaddingValues(top = 10.dp, bottom = 10.dp, start = 12.dp, end = 42.dp),
+ contentPadding = PaddingValues(
+ top = 10.dp.applyScaleUp(),
+ bottom = 10.dp.applyScaleUp(),
+ start = 12.dp.applyScaleUp(),
+ end = 42.dp.applyScaleUp(),
+ ),
interactionSource = remember { MutableInteractionSource() },
placeholder = {
Text(stringResource(CommonStrings.common_message), style = defaultTypography)
@@ -193,7 +207,7 @@ fun TextComposer(
canSendMessage = composerCanSendMessage,
onSendMessage = onSendMessage,
composerMode = composerMode,
- modifier = Modifier.padding(end = 6.dp, bottom = 6.dp)
+ modifier = Modifier.padding(end = 6.dp.applyScaleUp(), bottom = 6.dp.applyScaleUp())
)
}
}
@@ -253,7 +267,7 @@ private fun EditingModeView(
tint = ElementTheme.materialColors.secondary,
modifier = Modifier
.padding(vertical = 8.dp)
- .size(16.dp),
+ .size(16.dp.applyScaleUp()),
)
Text(
stringResource(CommonStrings.common_editing),
@@ -270,7 +284,7 @@ private fun EditingModeView(
tint = ElementTheme.materialColors.secondary,
modifier = Modifier
.padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 12.dp)
- .size(16.dp)
+ .size(16.dp.applyScaleUp())
.clickable(
enabled = true,
onClick = onResetComposerMode,
@@ -333,7 +347,7 @@ private fun ReplyToModeView(
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier
.padding(end = 4.dp, top = 4.dp, start = 16.dp, bottom = 16.dp)
- .size(16.dp)
+ .size(16.dp.applyScaleUp())
.clickable(
enabled = true,
onClick = onResetComposerMode,
@@ -351,13 +365,13 @@ private fun AttachmentButton(
) {
Surface(
modifier
- .size(30.dp)
+ .size(30.dp.applyScaleUp())
.clickable(onClick = onClick),
shape = CircleShape,
color = ElementTheme.colors.iconPrimary
) {
Image(
- modifier = Modifier.size(12.5f.dp),
+ modifier = Modifier.size(12.5f.dp.applyScaleUp()),
painter = painterResource(R.drawable.ic_add_attachment),
contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment),
contentScale = ContentScale.Inside,
@@ -381,10 +395,10 @@ private fun BoxScope.SendButton(
modifier = modifier
.clip(CircleShape)
.background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent)
- .size(30.dp)
+ .size(30.dp.applyScaleUp())
.align(Alignment.BottomEnd)
.applyIf(composerMode !is MessageComposerMode.Edit, ifTrue = {
- padding(start = 1.dp) // Center the arrow in the circle
+ padding(start = 1.dp.applyScaleUp()) // Center the arrow in the circle
})
.clickable(
enabled = canSendMessage,
@@ -404,7 +418,7 @@ private fun BoxScope.SendButton(
else -> stringResource(CommonStrings.action_send)
}
Icon(
- modifier = Modifier.size(16.dp),
+ modifier = Modifier.size(16.dp.applyScaleUp()),
resourceId = iconId,
contentDescription = contentDescription,
// Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary
@@ -415,7 +429,7 @@ private fun BoxScope.SendButton(
@DayNightPreviews
@Composable
-fun TextComposerSimplePreview() = ElementPreview {
+internal fun TextComposerSimplePreview() = ElementPreview {
Column {
TextComposer(
onSendMessage = {},
@@ -446,7 +460,7 @@ fun TextComposerSimplePreview() = ElementPreview {
@DayNightPreviews
@Composable
-fun TextComposerEditPreview() = ElementPreview {
+internal fun TextComposerEditPreview() = ElementPreview {
TextComposer(
onSendMessage = {},
onComposerTextChange = {},
@@ -459,7 +473,7 @@ fun TextComposerEditPreview() = ElementPreview {
@DayNightPreviews
@Composable
-fun TextComposerReplyPreview() = ElementPreview {
+internal fun TextComposerReplyPreview() = ElementPreview {
Column {
TextComposer(
onSendMessage = {},
diff --git a/libraries/textcomposer/src/main/res/values-ru/translations.xml b/libraries/textcomposer/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..9f7324f086
--- /dev/null
+++ b/libraries/textcomposer/src/main/res/values-ru/translations.xml
@@ -0,0 +1,18 @@
+
+
+ "Прикрепить файл"
+ "Переключить список маркеров"
+ "Переключить блок кода"
+ "Сообщение"
+ "Применить жирный шрифт"
+ "Применить курсивный формат"
+ "Применить формат зачеркивания"
+ "Применить формат подчеркивания"
+ "Переключение полноэкранного режима"
+ "Отступ"
+ "Применить встроенный формат кода"
+ "Установить ссылку"
+ "Переключить нумерованный список"
+ "Переключить цитату"
+ "Без отступа"
+
diff --git a/libraries/textcomposer/src/main/res/values-sk/translations.xml b/libraries/textcomposer/src/main/res/values-sk/translations.xml
index a5f42a60f8..26ac1df436 100644
--- a/libraries/textcomposer/src/main/res/values-sk/translations.xml
+++ b/libraries/textcomposer/src/main/res/values-sk/translations.xml
@@ -1,5 +1,6 @@
+ "Pridať prílohu"
"Prepnúť zoznam odrážok"
"Prepnúť blok kódu"
"Správa…"
diff --git a/libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml b/libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..93777d4ca5
--- /dev/null
+++ b/libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,17 @@
+
+
+ "新增附件"
+ "切換項目編號"
+ "切換程式碼區塊"
+ "訊息"
+ "套用粗體"
+ "套用斜體"
+ "套用刪除線"
+ "套用底線"
+ "切換全螢幕模式"
+ "增加縮排"
+ "設定連結"
+ "切換數字編號"
+ "切換引用"
+ "減少縮排"
+
diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt
index f273c2dd64..62c87b39bb 100644
--- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt
+++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt
@@ -68,6 +68,14 @@ object ElementTheme {
*/
val typography: TypographyTokens = TypographyTokens
+ /**
+ * Material 3 [Typography] tokens. In Figma, these have the `M3 Typography/` prefix.
+ */
+ val materialTypography: Typography
+ @Composable
+ @ReadOnlyComposable
+ get()= MaterialTheme.typography
+
/**
* Returns whether the theme version used is the light or the dark one.
*/
diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt
index b797dab86a..2e705c8c79 100644
--- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt
+++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt
@@ -17,6 +17,8 @@
package io.element.android.libraries.theme
import androidx.compose.ui.graphics.Color
+import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens
+import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens
// =================================================================================================
// IMPORTANT!
@@ -26,3 +28,6 @@ import androidx.compose.ui.graphics.Color
// =================================================================================================
val LinkColor = Color(0xFF0086E6)
+
+val SnackBarLabelColorLight = LightDesignTokens.colorGray700
+val SnackBarLabelColorDark = DarkDesignTokens.colorGray700
diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt
index d211869f71..3d359594e1 100644
--- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt
+++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt
@@ -91,7 +91,7 @@ internal val materialColorSchemeDark = darkColorScheme(
@Preview
@Composable
-fun ColorsSchemePreviewLight() = ColorsSchemePreview(
+internal fun ColorsSchemePreviewLight() = ColorsSchemePreview(
Color.Black,
Color.White,
materialColorSchemeLight,
@@ -99,7 +99,7 @@ fun ColorsSchemePreviewLight() = ColorsSchemePreview(
@Preview
@Composable
-fun ColorsSchemePreviewDark() = ColorsSchemePreview(
+internal fun ColorsSchemePreviewDark() = ColorsSchemePreview(
Color.White,
Color.Black,
materialColorSchemeDark,
diff --git a/libraries/theme/src/main/res/drawable/ic_chat.xml b/libraries/theme/src/main/res/drawable/ic_chat.xml
deleted file mode 100644
index 1fef824a1d..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_chat.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_check.xml b/libraries/theme/src/main/res/drawable/ic_check.xml
deleted file mode 100644
index e92733095b..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_check.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_check_circle.xml b/libraries/theme/src/main/res/drawable/ic_check_circle.xml
deleted file mode 100644
index ad3aacbe28..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_check_circle.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_chevron.xml b/libraries/theme/src/main/res/drawable/ic_chevron.xml
deleted file mode 100644
index 4ecd3f16b0..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_chevron.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_close.xml b/libraries/theme/src/main/res/drawable/ic_close.xml
deleted file mode 100644
index f334767b67..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_computer.xml b/libraries/theme/src/main/res/drawable/ic_computer.xml
deleted file mode 100644
index e2748c2d4a..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_computer.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_delete.xml b/libraries/theme/src/main/res/drawable/ic_delete.xml
deleted file mode 100644
index 413a570210..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_delete.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_error.xml b/libraries/theme/src/main/res/drawable/ic_error.xml
deleted file mode 100644
index d978824039..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_error.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_info.xml b/libraries/theme/src/main/res/drawable/ic_info.xml
deleted file mode 100644
index 69865e325a..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_info.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_lock.xml b/libraries/theme/src/main/res/drawable/ic_lock.xml
deleted file mode 100644
index 2ada59e82f..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_lock.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_mobile.xml b/libraries/theme/src/main/res/drawable/ic_mobile.xml
deleted file mode 100644
index f2c46be357..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_mobile.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_thread.xml b/libraries/theme/src/main/res/drawable/ic_thread.xml
deleted file mode 100644
index d3293fab5a..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_thread.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_user.xml b/libraries/theme/src/main/res/drawable/ic_user.xml
deleted file mode 100644
index 5f61985ce4..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_user.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml b/libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml
deleted file mode 100644
index 3f20783ee4..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_visibility_visible.xml b/libraries/theme/src/main/res/drawable/ic_visibility_visible.xml
deleted file mode 100644
index 1283a1512b..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_visibility_visible.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/theme/src/main/res/drawable/ic_web_browser.xml b/libraries/theme/src/main/res/drawable/ic_web_browser.xml
deleted file mode 100644
index 080ce75905..0000000000
--- a/libraries/theme/src/main/res/drawable/ic_web_browser.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml
index 10694181da..0f5259e663 100644
--- a/libraries/ui-strings/src/main/res/values-de/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-de/translations.xml
@@ -94,7 +94,7 @@
"Passwort"
"Personen"
"Permalink"
- "Datenschutzerklärung"
+ "Datenschutzerklärung"
"Reaktionen"
"Aktualisiere…"
"Auf %1$s antworten"
@@ -145,7 +145,7 @@
"%1$s konnte nicht auf deinen Standort zugreifen. Bitte versuche es später erneut."
"Einige Nachrichten wurden nicht gesendet"
"Entschuldigung, ein Fehler ist aufgetreten."
- "🔐️ Besuchen Sie mich auf %1$s"
+ "🔐️ Besuche mich auf %1$s"
"Hey, sprich mit mir auf %1$s: %2$s"
"Bist du sicher, dass du diesen Raum verlassen willst? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr beitreten, auch du nicht."
"Bist du dir sicher, dass du den Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne eine Einladung nicht mehr beitreten."
@@ -164,10 +164,10 @@
"Neu"
"Teile Analyse-Daten"
"Medienauswahl fehlgeschlagen, bitte versuche es erneut."
- "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuchen Sie es erneut."
+ "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."
"Hochladen von Medien fehlgeschlagen, bitte versuchen Sie es erneut."
"Dies ist ein einmaliger Vorgang, danke fürs Warten."
- "Deinen Account einrichten"
+ "Dein Konto einrichten"
"Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"
"Standort teilen"
"Meinen Standort teilen"
@@ -184,7 +184,7 @@
"Fehler"
"Erfolg"
"Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."
- "Sie können alle unsere Nutzerbedingungen %1$s lesen."
+ "Du kannst alle unsere Nutzerbedingungen %1$s lesen."
"hier"
"Nutzer blockieren"
diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml
new file mode 100644
index 0000000000..d4830e9127
--- /dev/null
+++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml
@@ -0,0 +1,213 @@
+
+
+ "Скрыть пароль"
+ "Отправить файлы"
+ "Показать пароль"
+ "Меню пользователя"
+ "Разрешить"
+ "Назад"
+ "Отмена"
+ "Выбрать фото"
+ "Очистить"
+ "Закрыть"
+ "Полная проверка"
+ "Подтвердить"
+ "Продолжить"
+ "Копировать"
+ "Скопировать ссылку"
+ "Скопировать ссылку в сообщение"
+ "Создать"
+ "Создать комнату"
+ "Отклонить"
+ "Отключить"
+ "Готово"
+ "Редактировать"
+ "Включить"
+ "Забыли пароль?"
+ "Переслать"
+ "Пригласить"
+ "Пригласить друзей"
+ "Пригласить друзей в %1$s"
+ "Пригласите пользователей в %1$s"
+ "Приглашения"
+ "Подробнее"
+ "Выйти"
+ "Покинуть комнату"
+ "Далее"
+ "Нет"
+ "Не сейчас"
+ "Ок"
+ "Открыть с помощью"
+ "Быстрый ответ"
+ "Цитата"
+ "Реакция"
+ "Удалить"
+ "Ответить"
+ "Сообщить об ошибке"
+ "Пожаловаться на содержание"
+ "Повторить"
+ "Повторите расшифровку"
+ "Сохранить"
+ "Поиск"
+ "Отправить"
+ "Отправить сообщение"
+ "Поделиться"
+ "Поделиться ссылкой"
+ "Пропустить"
+ "Начать"
+ "Начать чат "
+ "Начать подтверждение"
+ "Нажмите, чтобы загрузить карту"
+ "Сделать фото"
+ "Показать источник"
+ "Да"
+ "О приложении"
+ "Политика допустимого использования"
+ "Аналитика"
+ "Аудио"
+ "Пузыри"
+ "Авторское право"
+ "Создание комнаты…"
+ "Покинул комнату"
+ "Ошибка расшифровки"
+ "Для разработчика"
+ "(изменено)"
+ "Редактирование"
+ "%1$s%2$s"
+ "Шифрование включено"
+ "Ошибка"
+ "Файл"
+ "Файл сохранен в «Загрузки»"
+ "Переслать сообщение"
+ "GIF"
+ "Изображения"
+ "Идентификатор Matrix ID не найден, приглашение может быть не получено."
+ "Покинуть комнату"
+ "Ссылка скопирована в буфер обмена"
+ "Загрузка…"
+ "Сообщение"
+ "Оформление сообщений"
+ "Сообщение удалено"
+ "Современный"
+ "Без звука"
+ "Ничего не найдено"
+ "Не в сети"
+ "Пароль"
+ "Пользователи"
+ "Постоянная ссылка"
+ "Политика конфиденциальности"
+ "Реакции"
+ "Обновление…"
+ "Отвечает на %1$s"
+ "Сообщить об ошибке"
+ "Отчет отправлен"
+ "Название комнаты"
+ "например, название вашего проекта"
+ "Поиск человека"
+ "Результаты поиска"
+ "Безопасность"
+ "Выберите свой сервер"
+ "Отправка…"
+ "Сервер не поддерживается"
+ "Адрес сервера"
+ "Настройки"
+ "Делится местонахождением"
+ "Начало чата…"
+ "Стикер"
+ "Успешно"
+ "Рекомендации"
+ "Синхронизация"
+ "Уведомление о третьей стороне"
+ "Тема"
+ "О чем эта комната?"
+ "Невозможно расшифровать"
+ "Не удалось отправить приглашения одному или нескольким пользователям."
+ "Не удалось отправить приглашение(я)"
+ "Включить звук"
+ "Неподдерживаемое событие"
+ "Имя пользователя"
+ "Проверка отменена"
+ "Проверка завершена"
+ "Видео"
+ "Ожидание…"
+ "Подтверждение"
+ "Предупреждение"
+ "Деятельность"
+ "Флаги"
+ "Еда и напитки"
+ "Животные и природа"
+ "Объекты"
+ "Смайлы и люди"
+ "Путешествия и места"
+ "Символы"
+ "Не удалось создать постоянную ссылку"
+ "Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже."
+ "Не удалось загрузить сообщения"
+ "%1$s не удалось получить доступ к вашему местоположению. Пожалуйста, повторите попытку позже."
+ "У %1$s нет разрешения на доступ к вашему местоположению. Вы можете разрешить доступ в Настройках."
+ "У %1$s нет разрешения на доступ к вашему местоположению. Разрешите доступ ниже."
+ "Некоторые сообщения не были отправлены"
+ "Извините, произошла ошибка"
+ "🔐️ Присоединяйтесь ко мне в %1$s"
+ "Привет, поговори со мной по %1$s: %2$s"
+ "Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас."
+ "Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения."
+ "Вы уверены, что хотите покинуть комнату?"
+ "%1$s Android"
+
+ - "%1$d участник"
+ - "%1$d участников"
+ - "%1$d участников"
+
+
+ - "%d голос"
+ - "%d голоса"
+ - "%d голосов"
+
+ "Rageshake сообщит об ошибке"
+ "Кажется, вы трясли телефон. Хотите открыть экран отчета об ошибке?"
+ "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения."
+ "Причина, по которой вы пожаловались на этот контент"
+ "Это начало %1$s."
+ "Это начало разговора."
+ "Новый"
+ "Делитесь данными аналитики"
+ "Не удалось выбрать носитель, попробуйте еще раз."
+ "Не удалось обработать медиафайл для загрузки, попробуйте еще раз."
+ "Не удалось загрузить медиафайлы, попробуйте еще раз."
+ "Это одноразовый процесс, спасибо, что подождали."
+ "Настройка учетной записи."
+ "Дополнительные параметры"
+ "Аудио и видео звонки"
+ "Прямые чаты"
+ "Включить уведомления на данном устройстве"
+ "Групповые чаты"
+ "Упоминания"
+ "Все"
+ "Упоминания"
+ "Уведомить меня"
+ "Уведомить меня в @room"
+ "Чтобы получать уведомления, измените свой %1$s."
+ "Настройки системы"
+ "Системные уведомления выключены"
+ "Уведомления"
+ "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя"
+ "Поделиться местоположением"
+ "Поделиться моим местоположением"
+ "Открыть в Apple Maps"
+ "Открыть в Google Картах"
+ "Открыть в OpenStreetMap"
+ "Поделиться этим местоположением"
+ "Местоположение"
+ "Rageshake"
+ "Порог обнаружения"
+ "Основные"
+ "Версия: %1$s (%2$s)"
+ "en"
+ "Ошибка"
+ "Успешно"
+ "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы."
+ "Вы можете ознакомиться со всеми нашими условиями %1$s."
+ "здесь"
+ "Заблокировать пользователя"
+
diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml
index 654f3d7cbe..14dd5626c8 100644
--- a/libraries/ui-strings/src/main/res/values-sk/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml
@@ -40,6 +40,7 @@
"Otvoriť pomocou"
"Rýchla odpoveď"
"Citovať"
+ "Reagovať"
"Odstrániť"
"Odpovedať"
"Nahlásiť chybu"
@@ -94,6 +95,9 @@
"Heslo"
"Ľudia"
"Trvalý odkaz"
+ "Výsledné hlasovanie: %1$s"
+ "Celkový počet hlasov: %1$s"
+ "Výsledky sa zobrazia po ukončení ankety"
"Zásady ochrany osobných údajov"
"Reakcie"
"Obnovuje sa…"
@@ -143,6 +147,8 @@
"%1$s nedokázal načítať mapu. Skúste to prosím neskôr."
"Načítanie správ zlyhalo"
"%1$s nemohol získať prístup k vašej polohe. Skúste to prosím neskôr."
+ "%1$s nemá povolenie na prístup k vašej polohe. Prístup môžete zapnúť v Nastaveniach."
+ "%1$s nemá povolenie na prístup k vašej polohe. Povoľte prístup nižšie."
"Niektoré správy neboli odoslané"
"Prepáčte, vyskytla sa chyba"
"🔐️ Pripojte sa ku mne na %1$s"
@@ -156,6 +162,11 @@
- "%1$d členovia"
- "%1$d členov"
+
+ - "1 hlas"
+ - "%d hlasy"
+ - "%d hlasov"
+
"Zúrivo potriasť pre nahlásenie chyby"
"Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s nahlásením chýb?"
"Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy."
@@ -169,7 +180,18 @@
"Nepodarilo sa nahrať médiá, skúste to prosím znova."
"Ide o jednorazový proces, ďakujeme za trpezlivosť."
"Nastavenie vášho účtu."
+ "Ďalšie nastavenia"
+ "Audio a video hovory"
+ "Priame konverzácie"
+ "Pri priamych rozhovoroch ma upozorniť na"
+ "Pri skupinových rozhovoroch ma upozorniť na"
"Povoliť oznámenia na tomto zariadení"
+ "Skupinové rozhovory"
+ "Zmienky"
+ "Všetky"
+ "Zmienky"
+ "Upozorniť ma na"
+ "Upozorniť ma na @miestnosť"
"Ak chcete dostávať oznámenia, zmeňte prosím svoje %1$s."
"nastavenia systému"
"Systémové oznámenia sú vypnuté"
diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
new file mode 100644
index 0000000000..701a6243ac
--- /dev/null
+++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
@@ -0,0 +1,169 @@
+
+
+ "隱藏密碼"
+ "傳送檔案"
+ "顯示密碼"
+ "使用者選單"
+ "接受"
+ "返回"
+ "取消"
+ "選擇照片"
+ "清除"
+ "關閉"
+ "完成驗證"
+ "確認"
+ "繼續"
+ "複製"
+ "複製連結"
+ "建立"
+ "建立聊天室"
+ "停用"
+ "完成"
+ "編輯"
+ "啟用"
+ "忘記密碼?"
+ "轉寄"
+ "邀請"
+ "邀請朋友"
+ "邀請朋友使用%1$s"
+ "邀請夥伴使用%1$s"
+ "邀請"
+ "了解更多"
+ "離開"
+ "離開聊天室"
+ "下一個"
+ "否"
+ "以後再說"
+ "OK"
+ "用其他方式開啟"
+ "快速回覆"
+ "引用"
+ "回應"
+ "移除"
+ "回覆"
+ "檢舉內容"
+ "再試一次"
+ "再次嘗試解密"
+ "儲存"
+ "搜尋"
+ "傳送"
+ "傳送訊息"
+ "分享"
+ "分享連結"
+ "跳過"
+ "開始"
+ "開始聊天"
+ "開始驗證"
+ "點擊以載入地圖"
+ "拍照"
+ "檢視原始碼"
+ "是"
+ "關於"
+ "分析"
+ "音訊"
+ "著作權"
+ "正在建立聊天室…"
+ "離開聊天室"
+ "解密錯誤"
+ "開發者選項"
+ "(已編輯)"
+ "編輯中"
+ "已啟用加密"
+ "錯誤"
+ "檔案"
+ "檔案已儲存至 Downloads"
+ "訊息轉寄"
+ "GIF"
+ "圖片"
+ "找不到此 Matrix ID,因此可能沒有人會收到邀請。"
+ "正在離開聊天室"
+ "連結已複製到剪貼簿"
+ "載入中…"
+ "訊息"
+ "訊息布局"
+ "訊息已移除"
+ "現代"
+ "關閉通知"
+ "查無結果"
+ "離線"
+ "密碼"
+ "夥伴"
+ "永久連結"
+ "結果將在投票結束後公佈"
+ "隱私權政策"
+ "回應"
+ "重新整理…"
+ "正在回覆%1$s"
+ "聊天室名稱"
+ "範例:您的計畫名稱"
+ "搜尋結果"
+ "選擇您的伺服器"
+ "傳送中…"
+ "伺服器 URL"
+ "設定"
+ "貼圖"
+ "成功"
+ "建議"
+ "同步中"
+ "主題"
+ "無法解密"
+ "無法發送邀請給一或多個使用者。"
+ "無法發送邀請"
+ "開啟通知"
+ "使用者名稱"
+ "驗證已取消"
+ "驗證完成"
+ "影片"
+ "等待中…"
+ "確認"
+ "警告"
+ "活動"
+ "旗幟"
+ "食物與飲料"
+ "動物與大自然"
+ "物品"
+ "表情與人物"
+ "旅行與景點"
+ "標誌"
+ "無法建立永久連結"
+ "%1$s無法載入地圖。請稍後再試。"
+ "無法載入訊息"
+ "%1$s無法取得您的位置。請稍後再試。"
+ "有些訊息尚未傳送"
+ "您確定要離開聊天室嗎?這裡只有您一個人。如果您離開了,包含您在內的所有人都無法再進入此聊天室。"
+ "您確定要離開聊天室嗎?此聊天室不是公開的,如果沒有收到邀請,您無法重新加入。"
+ "您確定要離開聊天室嗎?"
+ "%1$s Android"
+
+ - "%1$d 位成員"
+
+
+ - "%d 票"
+
+ "檢舉這個內容的原因"
+ "新訊息"
+ "無法上傳媒體檔案,請稍後再試。"
+ "設定您的帳號"
+ "其他設定"
+ "私訊"
+ "在這個裝置上開啟通知"
+ "群組聊天"
+ "提及"
+ "提及"
+ "系統設定"
+ "已關閉系統通知"
+ "通知"
+ "分享位置"
+ "分享我的位置"
+ "在 Apple 地圖中開啟"
+ "在 Google 地圖中開啟"
+ "在開放街圖(OpenStreetMap) 中開啟"
+ "分享這個位置"
+ "位置"
+ "一般"
+ "版本:%1$s(%2$s)"
+ "zh-tw"
+ "錯誤"
+ "成功"
+ "封鎖使用者"
+
diff --git a/libraries/ui-strings/src/main/res/values/donottranslate.xml b/libraries/ui-strings/src/main/res/values/donottranslate.xml
deleted file mode 100755
index 910ce31c41..0000000000
--- a/libraries/ui-strings/src/main/res/values/donottranslate.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- …
- –
-
-
- Not implemented yet in ${app_name}
-
-
- Cut the slack from teams.
-
- Crash the application.
-
-
- © MapTiler © OpenStreetMap contributors
-
diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml
index 10952f4194..3b4c305ffc 100644
--- a/libraries/ui-strings/src/main/res/values/localazy.xml
+++ b/libraries/ui-strings/src/main/res/values/localazy.xml
@@ -40,6 +40,7 @@
"Open with"
"Quick reply"
"Quote"
+ "React"
"Remove"
"Reply"
"Report bug"
@@ -94,6 +95,9 @@
"Password"
"People"
"Permalink"
+ "Final votes: %1$s"
+ "Total votes: %1$s"
+ "Results will show after the poll has ended"
"Privacy policy"
"Reactions"
"Refreshing…"
@@ -143,8 +147,8 @@
"%1$s could not load the map. Please try again later."
"Failed loading messages"
"%1$s could not access your location. Please try again later."
- "To send a location, allow %1$s to access your location from its settings screen."
- "To send a location, allow %1$s to access your location in the next dialog."
+ "%1$s does not have permission to access your location. You can enable access in Settings."
+ "%1$s does not have permission to access your location. Enable access below."
"Some messages have not been sent"
"Sorry, an error occurred"
"🔐️ Join me on %1$s"
@@ -157,6 +161,10 @@
- "%1$d member"
- "%1$d members"
+
+ - "%d vote"
+ - "%d votes"
+
"Rageshake to report bug"
"You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"
"This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages."
@@ -170,7 +178,19 @@
"Failed uploading media, please try again."
"This is a one time process, thanks for waiting."
"Setting up your account."
+ "Additional settings"
+ "Audio and video calls"
+ "Direct chats"
+ "An error occurred while updating the notification setting."
+ "On direct chats, notify me for"
+ "On group chats, notify me for"
"Enable notifications on this device"
+ "Group chats"
+ "Mentions"
+ "All"
+ "Mentions"
+ "Notify me for"
+ "Notify me on @room"
"To receive notifications, please change your %1$s."
"system settings"
"System notifications turned off"
diff --git a/plugins/settings.gradle.kts b/plugins/settings.gradle.kts
index defcb6f17b..7e2ce1ea50 100644
--- a/plugins/settings.gradle.kts
+++ b/plugins/settings.gradle.kts
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+rootProject.name = "ElementX_plugins"
+
dependencyResolutionManagement {
repositories {
mavenCentral()
diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt
index 36a3a33e70..2e89afa70f 100644
--- a/plugins/src/main/kotlin/Versions.kt
+++ b/plugins/src/main/kotlin/Versions.kt
@@ -17,10 +17,50 @@
import org.gradle.api.JavaVersion
import org.gradle.jvm.toolchain.JavaLanguageVersion
-object Versions {
- const val versionCode = 100100
- const val versionName = "0.1.0"
+/**
+ * Version codes are quite sensitive, because there is a mix between bundle and APKs, and we have to take into
+ * account the future upgrade of Element Android.
+ * Max versionCode allowed by the PlayStore (for information):
+ * 2_100_000_000
+ * Current version code of EAx on the PlayStore, for the first uploaded beta (we cannot go below):
+ * ----1_001_000
+ * Current version code of EAx on the nightly:
+ * ----1_001_000
+ * Current version of Element Android (at some point EAx will replace this app) (v1.6.3)
+ * ----40_106_03a where a stands for the architecture: 1, 2, 3, 4 and 0 for the universal APK
+ * Current version of EAx distributed with Firebase app distribution:
+ * ----1_002_000
+ * Latest version of EAx distributed with Firebase app distribution (downgrading, so that's a problem)
+ * -------10_200
+ * Version when running the current debug build
+ * -------10_200
+ *
+ * So adding 4_000_000 to the current version Code computed here should be fine, and since the versionCode
+ * is multiplied by 10 in app/build.gradle.kts#L168:
+ * ```
+ * output.versionCode.set((output.versionCode.get() ?: 0) * 10 + abiCode))
+ * ```
+ * we will have:
+ * Release version:
+ * ---40_001_020
+ * Nightly version:
+ * ---40_001_020
+ * Debug version:
+ * ---40_010_200
+ */
+// Note: 2 digits max for each value
+private const val versionMajor = 0
+private const val versionMinor = 1
+
+// Note: even values are reserved for regular release, odd values for hotfix release.
+// When creating a hotfix, you should decrease the value, since the current value
+// is the value for the next regular release.
+private const val versionPatch = 2
+
+object Versions {
+ val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch
+ val versionName = "$versionMajor.$versionMinor.$versionPatch"
const val compileSdk = 33
const val targetSdk = 33
const val minSdk = 23
diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
index 88f499b993..fb082e27a7 100644
--- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
+++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt
@@ -103,8 +103,12 @@ fun DependencyHandlerScope.allLibrariesImpl() {
}
fun DependencyHandlerScope.allServicesImpl() {
+ // For analytics configuration, either use noop, or use the impl, with at least one analyticsproviders implementation
+ // implementation(project(":services:analytics:noop"))
implementation(project(":services:analytics:impl"))
implementation(project(":services:analyticsproviders:posthog"))
+ implementation(project(":services:analyticsproviders:sentry"))
+
implementation(project(":services:apperror:impl"))
implementation(project(":services:appnavstate:impl"))
implementation(project(":services:toolbox:impl"))
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
index 21d6648a41..a915e70046 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt
@@ -26,11 +26,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.core.view.WindowCompat
-import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
+import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
+import io.element.android.libraries.theme.ElementTheme
import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock
import kotlinx.coroutines.runBlocking
import java.io.File
@@ -39,15 +40,22 @@ class MainActivity : ComponentActivity() {
private val matrixAuthenticationService: MatrixAuthenticationService by lazy {
val baseDirectory = File(applicationContext.filesDir, "sessions")
-
+ val userAgentProvider = SimpleUserAgentProvider("MinimalSample")
+ val sessionStore = InMemorySessionStore()
RustMatrixAuthenticationService(
- context = applicationContext,
baseDirectory = baseDirectory,
- appCoroutineScope = Singleton.appScope,
coroutineDispatchers = Singleton.coroutineDispatchers,
- sessionStore = InMemorySessionStore(),
- clock = DefaultSystemClock(),
- userAgentProvider = SimpleUserAgentProvider("MinimalSample")
+ sessionStore = sessionStore,
+ userAgentProvider = userAgentProvider,
+ rustMatrixClientFactory = RustMatrixClientFactory(
+ context = applicationContext,
+ baseDirectory = baseDirectory,
+ appCoroutineScope = Singleton.appScope,
+ coroutineDispatchers = Singleton.coroutineDispatchers,
+ sessionStore = sessionStore,
+ userAgentProvider = userAgentProvider,
+ clock = DefaultSystemClock()
+ )
)
}
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
index f66de878fb..faaccc9b8e 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt
@@ -68,7 +68,7 @@ class RoomListScreen(
inviteStateDataSource = DefaultInviteStateDataSource(matrixClient, DefaultSeenInvitesStore(context), coroutineDispatchers),
leaveRoomPresenter = LeaveRoomPresenterImpl(matrixClient, RoomMembershipObserver(), coroutineDispatchers),
roomListDataSource = RoomListDataSource(
- roomSummaryDataSource = matrixClient.roomSummaryDataSource,
+ roomListService = matrixClient.roomListService,
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters),
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(
sp = stringProvider,
@@ -87,7 +87,6 @@ class RoomListScreen(
Singleton.appScope.launch {
withContext(coroutineDispatchers.io) {
matrixClient.getRoom(roomId)!!.use { room ->
- room.open()
room.timeline.paginateBackwards(20, 50)
}
}
@@ -114,7 +113,9 @@ class RoomListScreen(
}
onDispose {
Timber.w("Stop sync!")
- matrixClient.syncService().stopSync()
+ runBlocking {
+ matrixClient.syncService().stopSync()
+ }
}
}
}
diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt
index 5f8c6555a5..027da552fa 100644
--- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt
+++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt
@@ -17,19 +17,41 @@
package io.element.android.samples.minimal
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
-import io.element.android.libraries.matrix.impl.tracing.setupTracing
-import io.element.android.libraries.matrix.api.tracing.TracingConfigurations
+import io.element.android.libraries.core.meta.BuildMeta
+import io.element.android.libraries.core.meta.BuildType
+import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
+import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations
+import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
+import io.element.android.libraries.matrix.impl.tracing.RustTracingService
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.plus
-import timber.log.Timber
object Singleton {
+ private val buildMeta = BuildMeta(
+ isDebuggable = true,
+ buildType = BuildType.DEBUG,
+ applicationName = "EAX-Minimal",
+ applicationId = "io.element.android.samples.minimal",
+ lowPrivacyLoggingEnabled = false,
+ versionName = "0.1.0",
+ versionCode = 1,
+ gitRevision = "TODO", // BuildConfig.GIT_REVISION,
+ gitRevisionDate = "TODO", // BuildConfig.GIT_REVISION_DATE,
+ gitBranchName = "TODO", // BuildConfig.GIT_BRANCH_NAME,
+ flavorDescription = "TODO", // BuildConfig.FLAVOR_DESCRIPTION,
+ flavorShortDescription = "TODO", // BuildConfig.SHORT_FLAVOR_DESCRIPTION,
+ )
+
init {
- Timber.plant(Timber.DebugTree())
- setupTracing(TracingConfigurations.debug)
+ val tracingConfiguration = TracingConfiguration(
+ filterConfiguration = TracingFilterConfigurations.debug,
+ writesToLogcat = true,
+ writesToFilesConfiguration = WriteToFilesConfiguration.Disabled
+ )
+ RustTracingService(buildMeta).setupTracing(tracingConfiguration)
}
val appScope = MainScope() + CoroutineName("Minimal Scope")
diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt
index 9c6fb2d522..309a885ad2 100644
--- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt
+++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt
@@ -22,7 +22,10 @@ import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker
import kotlinx.coroutines.flow.Flow
interface AnalyticsService: AnalyticsTracker, ErrorTracker {
- fun getAvailableAnalyticsProviders(): List
+ /**
+ * Get the available analytics providers.
+ */
+ fun getAvailableAnalyticsProviders(): Set
/**
* Return a Flow of Boolean, true if the user has given their consent.
diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt
index 5639f954ac..42acd29b56 100644
--- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt
+++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt
@@ -56,8 +56,8 @@ class DefaultAnalyticsService @Inject constructor(
observeSessions()
}
- override fun getAvailableAnalyticsProviders(): List {
- return analyticsProviders.sortedBy { it.index }
+ override fun getAvailableAnalyticsProviders(): Set {
+ return analyticsProviders
}
override fun getUserConsent(): Flow {
diff --git a/services/analytics/noop/build.gradle.kts b/services/analytics/noop/build.gradle.kts
index a5678f5cb3..000434a05c 100644
--- a/services/analytics/noop/build.gradle.kts
+++ b/services/analytics/noop/build.gradle.kts
@@ -19,7 +19,7 @@ plugins {
}
android {
- namespace = "io.element.android.services.analytics.impl"
+ namespace = "io.element.android.services.analytics.noop"
}
anvil {
@@ -28,6 +28,7 @@ anvil {
dependencies {
implementation(libs.dagger)
+ implementation(projects.libraries.architecture)
implementation(projects.libraries.di)
api(projects.services.analytics.api)
}
diff --git a/services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt b/services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt
new file mode 100644
index 0000000000..f82e7ff550
--- /dev/null
+++ b/services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.services.analytics.noop
+
+import com.squareup.anvil.annotations.ContributesBinding
+import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
+import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
+import im.vector.app.features.analytics.plan.UserProperties
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.SingleIn
+import io.element.android.services.analytics.api.AnalyticsService
+import io.element.android.services.analyticsproviders.api.AnalyticsProvider
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import javax.inject.Inject
+
+@SingleIn(AppScope::class)
+@ContributesBinding(AppScope::class)
+class NoopAnalyticsService @Inject constructor(
+) : AnalyticsService {
+ override fun getAvailableAnalyticsProviders(): Set = emptySet()
+ override fun getUserConsent(): Flow = flowOf(false)
+ override suspend fun setUserConsent(userConsent: Boolean) = Unit
+ override fun didAskUserConsent(): Flow = flowOf(true)
+ override suspend fun setDidAskUserConsent() = Unit
+ override fun getAnalyticsId(): Flow = flowOf("")
+ override suspend fun setAnalyticsId(analyticsId: String) = Unit
+ override suspend fun onSignOut() = Unit
+ override suspend fun reset() = Unit
+ override fun capture(event: VectorAnalyticsEvent) = Unit
+ override fun screen(screen: VectorAnalyticsScreen) = Unit
+ override fun updateUserProperties(userProperties: UserProperties) = Unit
+ override fun trackError(throwable: Throwable) = Unit
+}
diff --git a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt
index 548f47d7ad..807c8d1413 100644
--- a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt
+++ b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt
@@ -20,11 +20,6 @@ import io.element.android.services.analyticsproviders.api.trackers.AnalyticsTrac
import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker
interface AnalyticsProvider: AnalyticsTracker, ErrorTracker {
- /**
- * Allow to sort providers, from lower index to higher index.
- */
- val index: Int
-
/**
* User friendly name.
*/
diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt
index 92e73195c0..fb2e341e1e 100644
--- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt
+++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt
@@ -16,7 +16,6 @@
package io.element.android.services.analyticsproviders.posthog
-import com.posthog.android.Options
import com.posthog.android.PostHog
import com.posthog.android.Properties
import com.squareup.anvil.annotations.ContributesMultibinding
@@ -29,14 +28,13 @@ import io.element.android.services.analyticsproviders.posthog.log.analyticsTag
import timber.log.Timber
import javax.inject.Inject
-private val REUSE_EXISTING_ID: String? = null
-private val IGNORED_OPTIONS: Options? = null
+// private val REUSE_EXISTING_ID: String? = null
+// private val IGNORED_OPTIONS: Options? = null
@ContributesMultibinding(AppScope::class)
class PosthogAnalyticsProvider @Inject constructor(
private val postHogFactory: PostHogFactory,
) : AnalyticsProvider {
- override val index = PosthogConfig.index
override val name = PosthogConfig.name
private var posthog: PostHog? = null
@@ -101,9 +99,11 @@ class PosthogAnalyticsProvider @Inject constructor(
* We avoid sending nulls as part of the UserProperties as this will reset the values across all devices.
* The UserProperties event has nullable properties to allow for clients to opt in.
*/
+ /*
private fun Map.toPostHogUserProperties(): Properties {
return Properties().apply {
putAll(this@toPostHogUserProperties.filter { it.value != null })
}
}
+ */
}
diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt
index 877fb7dc9a..96d8659b11 100644
--- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt
+++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt
@@ -17,7 +17,6 @@
package io.element.android.services.analyticsproviders.posthog
object PosthogConfig {
- const val index = 0
const val name = "Posthog"
const val postHogHost = "https://posthog.element.dev"
const val postHogApiKey = "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN"
diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt
index 8e64ca100d..a298bda1c5 100644
--- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt
+++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt
@@ -18,4 +18,4 @@ package io.element.android.services.analyticsproviders.posthog.log
import io.element.android.libraries.core.log.logger.LoggerTag
-val analyticsTag = LoggerTag("Analytics")
+internal val analyticsTag = LoggerTag("Posthog")
diff --git a/services/analyticsproviders/sentry/build.gradle.kts b/services/analyticsproviders/sentry/build.gradle.kts
new file mode 100644
index 0000000000..34c444eb7d
--- /dev/null
+++ b/services/analyticsproviders/sentry/build.gradle.kts
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+plugins {
+ id("io.element.android-library")
+ alias(libs.plugins.anvil)
+}
+
+android {
+ namespace = "io.element.android.services.analyticsproviders.sentry"
+}
+
+anvil {
+ generateDaggerFactories.set(true)
+}
+
+dependencies {
+ implementation(libs.dagger)
+ implementation(libs.sentry)
+ implementation(projects.libraries.core)
+ implementation(projects.libraries.di)
+ implementation(projects.services.analyticsproviders.api)
+}
diff --git a/libraries/push/impl/src/main/res/values/dimens.xml b/services/analyticsproviders/sentry/src/main/AndroidManifest.xml
similarity index 70%
rename from libraries/push/impl/src/main/res/values/dimens.xml
rename to services/analyticsproviders/sentry/src/main/AndroidManifest.xml
index ce2fee2015..079912fc00 100644
--- a/libraries/push/impl/src/main/res/values/dimens.xml
+++ b/services/analyticsproviders/sentry/src/main/AndroidManifest.xml
@@ -14,8 +14,13 @@
~ limitations under the License.
-->
-
+
- 50dp
+
+
+
+
-
+
diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt
new file mode 100644
index 0000000000..6bc4df426f
--- /dev/null
+++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.services.analyticsproviders.sentry
+
+import android.content.Context
+import com.squareup.anvil.annotations.ContributesMultibinding
+import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
+import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
+import im.vector.app.features.analytics.plan.UserProperties
+import io.element.android.libraries.core.meta.BuildMeta
+import io.element.android.libraries.core.meta.BuildType
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.ApplicationContext
+import io.element.android.services.analyticsproviders.api.AnalyticsProvider
+import io.element.android.services.analyticsproviders.sentry.log.analyticsTag
+import io.sentry.Sentry
+import io.sentry.SentryOptions
+import io.sentry.android.core.SentryAndroid
+import timber.log.Timber
+import javax.inject.Inject
+
+@ContributesMultibinding(AppScope::class)
+class SentryAnalyticsProvider @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val buildMeta: BuildMeta,
+) : AnalyticsProvider {
+ override val name = SentryConfig.name
+
+ override fun init() {
+ Timber.tag(analyticsTag.value).d("Initializing Sentry")
+ if (Sentry.isEnabled()) return
+ SentryAndroid.init(context) { options ->
+ options.dsn = SentryConfig.dns
+ options.beforeSend = SentryOptions.BeforeSendCallback { event, _ -> event }
+ options.tracesSampleRate = 1.0
+ options.isEnableUserInteractionTracing = true
+ options.environment = buildMeta.buildType.toSentryEnv()
+ options.diagnosticLevel
+ }
+ }
+
+ override fun stop() {
+ Timber.tag(analyticsTag.value).d("Stopping Sentry")
+ Sentry.close()
+ }
+
+ override fun capture(event: VectorAnalyticsEvent) {
+ }
+
+ override fun screen(screen: VectorAnalyticsScreen) {
+ }
+
+ override fun updateUserProperties(userProperties: UserProperties) {
+ }
+
+ override fun trackError(throwable: Throwable) {
+ Sentry.captureException(throwable)
+ }
+}
+
+private fun BuildType.toSentryEnv() = when (this) {
+ BuildType.RELEASE -> SentryConfig.envRelease
+ BuildType.NIGHTLY,
+ BuildType.DEBUG -> SentryConfig.envDebug
+}
diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt
new file mode 100644
index 0000000000..f2048b59f0
--- /dev/null
+++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.services.analyticsproviders.sentry
+
+object SentryConfig {
+ const val name = "Sentry"
+ const val dns = "https://32f7ff6a6e724f90838b7654042b2e81@sentry.tools.element.io/59"
+ const val envDebug = "DEBUG"
+ const val envRelease = "RELEASE"
+}
diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt
new file mode 100644
index 0000000000..f792009ee4
--- /dev/null
+++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.services.analyticsproviders.sentry.log
+
+import io.element.android.libraries.core.log.logger.LoggerTag
+
+internal val analyticsTag = LoggerTag("Sentry")
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 408c9e2934..751c65d388 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -36,6 +36,10 @@ dependencyResolutionManagement {
includeModule("com.github.matrix-org", "matrix-analytics-events")
}
}
+ // To have immediate access to Rust SDK versions
+ maven {
+ url = URI("https://s01.oss.sonatype.org/content/repositories/releases")
+ }
flatDir {
dirs("libraries/matrix/libs")
}
diff --git a/tests/testutils/build.gradle.kts b/tests/testutils/build.gradle.kts
index d7c17c7895..184bbc418a 100644
--- a/tests/testutils/build.gradle.kts
+++ b/tests/testutils/build.gradle.kts
@@ -30,4 +30,5 @@ dependencies {
implementation(libs.test.junit)
implementation(libs.coroutines.test)
implementation(projects.libraries.core)
+ implementation(libs.test.turbine)
}
diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt
new file mode 100644
index 0000000000..06b6b3d3ea
--- /dev/null
+++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+package io.element.android.tests.testutils
+
+import app.cash.turbine.Event
+import app.cash.turbine.ReceiveTurbine
+import app.cash.turbine.withTurbineTimeout
+import io.element.android.libraries.core.data.tryOrNull
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+
+/**
+ * Consume all items until timeout is reached waiting for an event or we receive terminal event.
+ * The timeout is applied for each event.
+ * @return the list of consumed items.
+ */
+suspend fun ReceiveTurbine.consumeItemsUntilTimeout(timeout: Duration = 100.milliseconds): List {
+ return consumeItemsUntilPredicate(timeout) { false }
+}
+
+/**
+ * Consume items until predicate is true, or timeout is reached waiting for an event, or we receive terminal event.
+ * The timeout is applied for each event.
+ * @return the list of consumed items.
+ */
+suspend fun ReceiveTurbine.consumeItemsUntilPredicate(
+ timeout: Duration = 100.milliseconds,
+ predicate: (T) -> Boolean,
+): List {
+ val items = ArrayList()
+ tryOrNull {
+ var foundItemOrFinished = false
+ while (!foundItemOrFinished) {
+ when (val event = withTurbineTimeout(timeout) { awaitEvent() }) {
+ is Event.Item -> {
+ items.add(event.value)
+ if (predicate(event.value)) {
+ foundItemOrFinished = true
+ }
+ }
+ Event.Complete, is Event.Error -> foundItemOrFinished = true
+ }
+ }
+ }
+ return items
+}
diff --git a/tests/uitests/build.gradle.kts b/tests/uitests/build.gradle.kts
index 729899c4f8..1881822691 100644
--- a/tests/uitests/build.gradle.kts
+++ b/tests/uitests/build.gradle.kts
@@ -42,7 +42,6 @@ dependencies {
testImplementation(libs.test.junit)
testImplementation(libs.test.parameter.injector)
testImplementation(projects.libraries.designsystem)
- androidTestImplementation(libs.test.junitext)
ksp(libs.showkase.processor)
kspTest(libs.showkase.processor)
diff --git a/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt b/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt
index 8a33430340..cb7795c05a 100644
--- a/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt
+++ b/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt
@@ -18,7 +18,6 @@ package io.element.android.tests.uitests
import android.app.Activity
import android.content.Intent
-import com.airbnb.android.showkase.models.Showkase
import com.airbnb.android.showkase.ui.ShowkaseBrowserActivity
fun openShowkase(activity: Activity) {
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 7870560dd4..500f0a57f7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:da4188dc606f0735fd4093acad34897c866d8c4d20b3e0ec0618685f7302cbf5
-size 9288
+oid sha256:3f18e74bfbebd69109f36a7154b20d3ee61071bff0d8ebf28f4b3b35c58e2938
+size 9256
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 7edd6f9ee7..17ed1eee99 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e27b1ea7bfa4eb97af3c2d433fbe3f5ca21458e4458d91b39c9c2bb3d7b02abc
-size 11306
+oid sha256:eea203b9527f7c3dc3692e7db3e9d5cee20ca08fddf04977c948933dc2784022
+size 11274
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index c87156e8c6..3bbf79781a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2bbeb21faf320226f42aad179b955952ad8e4b5d5645c45bfbbc02166baf0662
-size 9641
+oid sha256:b5c69ae7d27eb7024e9a2f9c7100b308562ef2fc702e28604098180cb02a3813
+size 9645
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 6391f1dec0..1b135effbf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:499e17b52d57f8b0d8984e32457b3922c0647be5b835c02a01ed927fe004ec4d
-size 11610
+oid sha256:62149a4d9b0dd62ddb56e1cdb24fe401f189fda05d17dd16f8c0bc511eab6561
+size 11616
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index a4d544ce2d..d4098db0e0 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c51726746993ad3ed0f6da9fe49a02b87ec518495e04f6cdb28ab4454dc72938
-size 25457
+oid sha256:862bbfc81d7c4ccc16e6d8d1db4f81306c0219b45b3630cd2f73dcabf0550b3b
+size 26007
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index c5627b9105..9c873827d1 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:14ffd433679cbadf1bb325257786a834fce3e362d2ed763c1823b0c958c0f38a
-size 27496
+oid sha256:32f331dd755437c67fd5251e3034b63d1e099f1032043b86b6f6f989680e390f
+size 28184
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index c4f2de8348..f92d333a61 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:85d3ea85f270fae4db7e14ab9c2c19a6c4f3a68e1c2112010471cb69cdb71b2b
-size 22071
+oid sha256:250a6ae8cb93c781d570c40054f34b21832434f9a692adf637cf9e2560c6a09e
+size 21538
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 1aecdf6d10..735efd3e6c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fd79483dea8ce19284069511ec96dfc68704a9ff2f1a33be312bbcdb387123c6
-size 26655
+oid sha256:087d4e2355b7b64f930c8e4c149f565b77304ba0f62fc16ce0ff99696dc51b5e
+size 27966
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 192e7b90a4..0c885e9c4d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:931423fea194fb1bede5cb3d73083fb65335716ca088fbda707f4f4727eae4dd
-size 28757
+oid sha256:702d61eabe690c6b7a259d3773ed7e25011b0a8a99c7a1109930c2ea78b234a2
+size 30106
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 7ae3d51e4a..05d952ea66 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bfa6e3d8698a2327d6e3cc14318856f221355581e6c661b92d2e215daf8f30cd
-size 22879
+oid sha256:85b0c428cf44532739245c8303cc2c86ab5e0c6ffc060871f1eef8e1bce9955b
+size 23123
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 02064ef869..482d85290c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:434f7619fb6bd337ede775fc343acc05950907987523acbd0f578624e5d26857
-size 49246
+oid sha256:5fd66b90f0566317a48565be56e75496a7b156fc96354ea61a0cc0a7f036625a
+size 49329
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 293c11b5c8..9dc5bdf600 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:93036a30e1af96805c20ea5d65b6d3eb40d2ee11f8f3420436e936dd5b4ca38b
-size 50201
+oid sha256:5b6dc784e88a7c9ef9d5812ee474e8cbc5cafc55dfd21adeb15d3ace99a41db0
+size 50340
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 772474c81f..bdd263e811 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:92fda75e246b46ab786da2f54f920bf96959618ac20cece5f6ca754f7fcf6914
-size 14161
+oid sha256:7e62ff59a909d9fc8c255236756a6f7f9d7efbb85e222de92843251367e2f774
+size 14150
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index b88f3011ca..7961ed97ba 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a2a005b84bbf1be15028d36e153e79eb5acbe41e65e3f274a6d78b22be95efb5
-size 28509
+oid sha256:a3b008e2364af89731ba0431b7a97998452aa4d0ab7b25cb3d5130737b017a9e
+size 28525
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 7cfc3e1c03..4cfb07c3ec 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6bb2d3d6e83f5b0ca17809c415e636bf5dbfa846259e8fabd6c3afae37d7d1f9
-size 15231
+oid sha256:92a09f9c5915b444e92853809d6d42b0b2b76152f9fa58a1ac9856b15ee15373
+size 15222
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 965959b5ee..5ada26de58 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d3f7631f15054ac55688f76805a6f1924e79058881957b67284865b6508886cc
-size 29249
+oid sha256:d5ac056ba00dd474b08e1a1de0886c5e6989c12ea97a106e379fff9394aa5b90
+size 29263
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png
index 9e1ddd2ddb..e4ad0b75e4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d47034cafef80cdac5d702e52092f07ebc0bb96dbdd7f9f0c1ea2e302cbe9917
-size 33858
+oid sha256:7b42afece0186cbbcb9ca5b716c3c535147d27bf1c57cabf033339fe9100edeb
+size 33889
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png
index d50e567f72..5d7bb98cc3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:84ea420320bd406588cb4d9aa3fea79d63ec37fc31a00d4fa77a06a7c8499145
-size 35452
+oid sha256:248817743f94c09d5bb02580687f5d9150dcf22b1670289955197491f375ad65
+size 35640
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png
index 6595be90cb..687420defd 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d01eaadfd0b9508c44145ba2e6cd85a6a24fdb43f665188404f9da1cf3e03321
-size 86292
+oid sha256:a4f987d8b03013d3aacbcc2b5a7374148c29d2c24dc41211781db15a06f3d52a
+size 86365
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
index 3245d6013a..2e34348af3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:45f4c5a3edbf61d815a7b1221a73123b09c8767d15ae2d9ae04f11ae3defe697
-size 67911
+oid sha256:90a7a6bc46fea5d6a02c9e3b6a29018e51fe270fc5034fdd7f9045f6fbbbc1d9
+size 67916
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png
index 879d41bd8a..4f7bc094a8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6811324cfc868722c57a02f2544ffcbbb2f2247fc9284e4e6fe869e33447c7b1
-size 69939
+oid sha256:037e24e82436183ed2ff4138a997c232e331076dd5374958e11efcfa8063b4d2
+size 69962
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 74124f825f..c1299a57ea 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0169cdedfc56ba1b55f133ed41146a0ec3597123a88ed086f7584f06d235da26
-size 57732
+oid sha256:3f0c16ef939ce2625d8e6788c0ccba95a4bc85d96acf282bed01cd67db65a4de
+size 58090
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 50ee002c0c..c8928c442f 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a0cdeea89592fbad03e1ad29aa1616bd779403f022097ca11510ff9cda3457aa
-size 83656
+oid sha256:ab118e4c47a83d68eda51b8b077a3eb55f0b5ad829b670be56ddddb99545c2a3
+size 83648
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 6c4a4bcaba..49fc604d5f 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f61e37505ee1276d894641647ec34323f415f4a3cdc699de01579a92255c2a4e
-size 60941
+oid sha256:d016b99c433497a620543a4cdb1eae7207ca94bcd434b712b81d4ff72a886296
+size 61485
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index c1f3859c0a..1fd2f7aa17 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:630fa772c15d8f16461655b2c2763d8ca85dc29812fea7d01366d68e3a446eaf
-size 86660
+oid sha256:3cf250f67844c383047ca24b496f8539360467f8fb16c0623fce9c9fff942c1e
+size 86880
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 49d7942204..deccc2399a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5312bd01a41dc0d1b022b6ce7258435d1cd9de75bd4231ce7f86ed6f84de2e4b
-size 26306
+oid sha256:1013f54744e6f5e31bbba2bf0567e2a9cfe080e6821f81153f31c8588c88a29e
+size 27219
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index bd930af4d9..2c6bddd66a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1b9b88deb5557da8b4670231ee41aa737543244d61abfd399d7de0f6d4496dde
-size 27439
+oid sha256:322b47618d1ac52742bd163d1e588ebb222e2d709ee92a6e0d7746efef975293
+size 29016
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png
index f300f92921..14e58b56ce 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:16de62092834bf803c8165e974f45e14ccfc0128a3e74295a58eef965abc10c5
-size 301336
+oid sha256:6d2eab27a1132893542b240399a4003b4049b72903a822077f69dd9dafd26f71
+size 299106
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png
index 7465768560..d38be285ba 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6838e81cc5f2755ff76de7254e2c8bb445b76662d7ba9b4c83443b2c2ed03029
-size 406044
+oid sha256:fbad2f74e329a424e75ef96cb0c427a7baba044f3267346756324c39ebd6add6
+size 404196
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index ddc9420223..d4db900fe9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:07e722c2936e1332168319059d5b4a553b1fc7f03da8aff40b005a05b203632d
-size 28679
+oid sha256:7ee847a64a963e1536e8fa118516cb58dd3ef450decbaafda3cae067bd44e8c4
+size 28447
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index cfe7f4094d..22db922bfd 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:eab30f7ec6ad8e289f4987d23f373ca8f96220a5d0762d46723058bca53d0a41
-size 33580
+oid sha256:0aa12e1e306c98dc03ce08acf178624c17622546ab32d93571d0e6f73530e95a
+size 33101
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index f0155d216f..07f66bcad7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6a344ef3f77b759e1d43733f85fc7ee9bee98d6eb3980ce13c435bb33c0703f0
-size 33720
+oid sha256:ad431cfafb873a27010ef2f5191efbbb7a7d43518f3fd72cc1d93bde3fbc5db4
+size 33246
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index a38e7f59a4..5a0c58a0f6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6e5874a0e224636c5ad13f4022e8f2ef3dff97206a5551734de560fbc40c562a
-size 14447
+oid sha256:f8edf269361969e35486f0bfdfcfe99c69c82adbeea87c5ccc7af244a68dfdbe
+size 14022
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index 8f6d7d3782..0a3714c60a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1e526f418f5c8d1d0c51509808e22548ce3c295752e7a1cf28cfe394fccc01f4
-size 28834
+oid sha256:36dbf0c8406baed1c3125173b0113e071e28e8370b7fe6ec7887687e59e98035
+size 28612
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 29cf018c14..76d31c9467 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:97639d0985e0303e3a58120fbb7a2bba67b4279d198140a9f4cf6b19aa74bed0
-size 29199
+oid sha256:d7c5c16830d017da9d76cdf9469fe7e2d7e718b5d1cb2408e15fdcf773d707d6
+size 29132
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 1acc83ec3c..8560c591fe 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f0372c5389850f853652ad40ad54069e456aa33e13335e733d779a8d4d384049
-size 35297
+oid sha256:791944236c8027c25bad58e2a6db65a72d310fdc54095ed7475701a9fad689c7
+size 35006
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png
index ddb0df10ed..07cd0f89ce 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:62f57ee9fc205539ede541baf8e4b887ef7776675c4760acd5b27545cd021df8
-size 35420
+oid sha256:adfadfbbcd6920bca70c435cbb68c83e18cfd9e15eac4545fa5f1e404b258255
+size 35121
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 0b5b749dfd..4b96dac84e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ea79f0de34ba0613fea7ba98c65f5ff925a336ddf2d6734ea77b1a2d559527f0
-size 14416
+oid sha256:1694a5589d39a7b4d7fcc823a0d6b2c86e9bb9c759ace8e5790df85ebc7ea3af
+size 14167
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png
index d7bead23cf..cee0955c2c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4b13ebc73e257d5f588f9e74d0a35bc9af951faece57556636378914786429e3
-size 29503
+oid sha256:5575ca96bb0b925109b5aad2207c304a0d1d54e8eeace250369e23982f23ec09
+size 29424
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index bbd5173671..62e1d90606 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5fcd5bd88a1aea468c8f5e08a3e07a323bd0e76f8db913611929c80184c2f68c
-size 53541
+oid sha256:014ace4752ba1f47272fd5320aa567a3b825c159548b36a1cdea34eb933cf0f0
+size 52332
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 418e6be50b..c4a75a4b80 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d3a5f5dcad110d0fe9cc1e81537319feac59c7d25084d9b77ed22c44969a7237
-size 48499
+oid sha256:ef34ff577d23711414e082e08ad82548c307541ae384f695317232d66cd2e493
+size 50369
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index f3aeeb5b53..a1bee8e1d4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ee4f54670538988698e947dd42d6d3bc585f743684e83202d48f8d02d1bdf1f8
-size 49214
+oid sha256:9afbaa0b1fd0ae0e98db92688bba574e4279af51769bbe20e8c9bd2fe3cd6714
+size 51087
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index f669883f45..7089e6c668 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:442ff73662328311d0f8d9ef0000bf2be3b6141810b26f864847217081900411
-size 41007
+oid sha256:9fc1fd77ddc0abc5b26343e552895156797b6a3afa62fdebb277d04fc75273ed
+size 40830
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index f669883f45..7089e6c668 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:442ff73662328311d0f8d9ef0000bf2be3b6141810b26f864847217081900411
-size 41007
+oid sha256:9fc1fd77ddc0abc5b26343e552895156797b6a3afa62fdebb277d04fc75273ed
+size 40830
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index e123bbbe3f..23026cee2b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f20e6a88d40320286d73dfa346a02783a00b1fb4a10c804c2f147e2f909c5a2a
-size 56083
+oid sha256:0219335bf2f3be0a4a785d7ca2e05700278401aa090d5972a76ce077cfbe5be6
+size 55406
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 816e5fe57d..28647b48d9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c25bf522377a036e8faae35e8afe458d82cf3c761e2df237a1a4d5785f44e188
-size 50202
+oid sha256:57da53bc60d35949fc3c762e0c2e57a73552a065107543819791610ea3aea327
+size 52509
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index a57d29443c..e44fc33e53 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:904a991dd0b753af68f0ab626d1f236dc9ea273a35f100475cadad8f4332d25d
-size 50896
+oid sha256:94b57a5760b3afb973af466a8eec26c40e8e70b5790061fbb343320e359e12d8
+size 53103
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index d61deff1b1..024b035f6d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:98ff0c7db1cd224a6d9897b1daf49f57fc659b25e60a9d670e8b1c66811d4fed
-size 42505
+oid sha256:c2e9ffe60a426543aacb68bdb73d7019d9271673762edd3e4011b0ac445fadc1
+size 43067
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
index d61deff1b1..024b035f6d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:98ff0c7db1cd224a6d9897b1daf49f57fc659b25e60a9d670e8b1c66811d4fed
-size 42505
+oid sha256:c2e9ffe60a426543aacb68bdb73d7019d9271673762edd3e4011b0ac445fadc1
+size 43067
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index d36f847e7b..feca962b5c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:42d22676a81b8c1f313f2f21814b787c54fa52f8b344207b509fbb798f2f58a1
-size 20075
+oid sha256:8b4d93b1444cb814fe0d8471679dc492f10b78c2ba101884491bef16a567aba6
+size 20704
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 292f5d9f30..ca5aaa3076 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:34645175d5acb2915d04b9306d648a287cae769bffcae7ecaffd293d43580683
-size 29023
+oid sha256:35e9b3412e3a64ec302899f44185d8a82680506be71bc4a8aa7b4705a1f9f6fb
+size 29736
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index fe795d5c26..1e9705d92b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:56f203dce2ed8ff23a75eb66bcb116fb4af61b63b1ad095ecebf6d97ba557a2c
-size 31392
+oid sha256:a68d387ac86cadb030b44153ebb07864b59194d87465012fe5c45e5c5b6dd8cc
+size 32227
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index 1393c4aa2a..3337e0b854 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1d23fb856aa3402cebf5c976fe32f0475f2f58b37a9ac9d028b0164dae4de397
-size 13357
+oid sha256:ba63eb801cfe25cc2ec7dab0b1277757b1f608eff5a596157a8c2c457e2bbdd0
+size 12568
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 7ab75f845d..0f4c367ecf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e748443031dd55fdf60f13debd2484f8169b7eb23980786ad73898004c7bcbfe
-size 19615
+oid sha256:aec6ed6095cbdf2eabc46db41e4a6d5513a7a1a8f4610a0a68dfdbc006fea354
+size 21016
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 7b458addc7..22146dd734 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5c2419ccf59c98899071e705b63feb83e8976acca53ccb8f375551aa056f9950
-size 29204
+oid sha256:6dd7b26eca3d0c40bd38d02833c0c43c348608574997a5e35d91c61c19928b76
+size 30473
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index c64c365b53..f3933c1a22 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:598f0a995896c32b1755951779f3784dbbe5062b7039bb8952d383acf2399efd
-size 31871
+oid sha256:fa450e9ad1db65fee62ad2fad4de118e1b1f335fce7b2e685d25063006521fb3
+size 33305
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
index c4d8cc0f03..ff0b013334 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9bf0bc49ba28e3e31414b1d1eb177c119529bc7c95209a8bbefafa6fc07a6fba
-size 12603
+oid sha256:b25270512a267079055363bdb6d54cb5444bbfcad4e60f2ca0e0466452b28ae4
+size 12238
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png
index fe104f1c2f..c092db7ce4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:69535debd585127a4ce8b490ef6682c2e6c3c4d16478e6b9e9687ee1c1133637
-size 20879
+oid sha256:84581aac943c5065f1e5438465ee7d1845555e68aa005d8b596542ce3830dc83
+size 21258
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png
index 09e7be69f8..5032df6d49 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:356756de9f08042c3c2f3033d3f8a39cd9b49c5cfbcfbc274933c3efedd80d3d
-size 34534
+oid sha256:d430ee7f8f505a7fb42bb4f1ac795fa339ff9048837ee9f4c4166c4df78004fb
+size 37584
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png
index 5311a17e6e..8c58e965e3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:51ac3c4bb27d78419f73b99cb24327514ce56a64a03bf74ce41f158c2c3bd516
-size 33605
+oid sha256:b7ffd042ba4e4a6b10f6c566765bf3fe4c992513f09169ca89a263983f162d79
+size 35994
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png
index fe104f1c2f..c092db7ce4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:69535debd585127a4ce8b490ef6682c2e6c3c4d16478e6b9e9687ee1c1133637
-size 20879
+oid sha256:84581aac943c5065f1e5438465ee7d1845555e68aa005d8b596542ce3830dc83
+size 21258
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png
index 960bd96e80..c9c6837a0e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1fa3b5aae9cee5e2fec8929697b0606b655c45b10a65bcd641da315e98c48e1e
-size 20951
+oid sha256:46ad7d3a46b54543f226e82fa4199e2e2de7e2e91748d663bed88dfb7afc5b61
+size 21350
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png
index 643983770c..49bfbea7e2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ce74fa2b0364763152e69dcfa4f8d598504b630fa1227f2fa685bc886ccd5afa
-size 19434
+oid sha256:6fb273e816484cb326ea4ba00948e749362542c1b964e23c5dc48b306b909136
+size 19843
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png
index 173360baef..7259132ad6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:12a78fbc2d2e84e93d40d1e075d8b46c5366f3d323dd85f27be286a64231f884
-size 32120
+oid sha256:3cb01d6f6f798c3cf2528db2b6d974b1007a7520ade43370082a1479365c41a7
+size 34934
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png
index 0fa0b9c5d2..c9f3716e82 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a1bd4187a3713153d6e9ed94594385c3806f59c4d36cc6f2867a38d630551505
-size 31302
+oid sha256:8862e4c3a185ffe807c1357c6a24aac4a9d6eac6405a73aa24b4772de67170dd
+size 33419
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png
index 643983770c..49bfbea7e2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ce74fa2b0364763152e69dcfa4f8d598504b630fa1227f2fa685bc886ccd5afa
-size 19434
+oid sha256:6fb273e816484cb326ea4ba00948e749362542c1b964e23c5dc48b306b909136
+size 19843
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png
index ce8b5bf468..652c6b6492 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9390a3b8111ab0d31b3bbf3b6bf8794432d135130b37404ce4a646a74369d85b
-size 19502
+oid sha256:91378a7a126d1d9f9f234368930635ce85db3666721dc32b0646effa59d0fdee
+size 19991
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 5eb7294406..8bcbbee378 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:364f107ffaa4844d0141361642ce3a494a187588f27be50b5fd27d44be21fa64
-size 8887
+oid sha256:016ca2c634b467ba6d98ed221506d5bbb250b58a5ea631a0076a572f745cfa84
+size 8710
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index f317c61b6e..50577b4cbf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8d0a5de3c4e09d76b6453ccc6ace5c690d540d945a62e630474932734209058a
-size 11716
+oid sha256:c129a4d13320fb3fec6039e37c802de33d7dd607faa3890f9f1b34f663eecc46
+size 11552
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 34c2f19861..6ace5373a3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9218d1d514342c400051bebb10ef458c99103fda618bba45e61a524c5a58eb63
-size 11903
+oid sha256:abe3498282504f7b0bfa3a2713ea556b7d4d361d5173ea56bcdadb37766d0b93
+size 11742
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 471e59a5b0..209d16749b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f19878925f3b5b377a91885540fb15d29a5b78d8be2282d64e8809af0bbf5ff4
-size 12195
+oid sha256:d52d7c6d6271577c80ad1ca170aeb9686d8bb572459212d6ef1fe97c490b382d
+size 12035
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index b56214c49f..1e5b0e65de 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c936d2d804bc9e98fcc49430f11ddaa572b05fc8d3a0df93ad6521ee8e78f708
-size 21806
+oid sha256:3e6442ca746462345d0573abfb72028a613b08d53f6a5e3f0ef3c5f09beab423
+size 19491
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6c5d1f2553
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:906b39149036cccadd5232c0373a6a94e039b374988ace3d732a5b63b7d81829
+size 21590
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index ea8bf3f1bd..d25793cf30 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ac01bc1992e3fa27950c7071cd3e8a06b94a608238d55972816fa2a1a3175e7c
-size 9448
+oid sha256:545fcfe023789a167a5ee2fe11e7f65426890d794fa08ed32ea4e9fe0fe5f59a
+size 9387
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 342978d4f7..bf88ad0eee 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e39c5ba30983b034886a6adc330d1510b3a48c40511697edcaf6530716c7ba2e
-size 12500
+oid sha256:b7ebe27e257108881e4869213ea754926fceb8a047ccb81120fc1a1df19279f4
+size 12444
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index c1fa61a838..2332b90a7e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4ff75e74c19280308878e3001a8791aaa735439cc667946fefd21070611630e
-size 12698
+oid sha256:ecb0ec0f6b9bffc5cd0ab19f85beb962610d5624652b8e6f21d8aedda69a0fb4
+size 12645
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 8ecfebb614..05ba2a726c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:065d2a09680e35540b870862ec0ba5c54182e016017938ef64e510b6132333c9
-size 13389
+oid sha256:989b630c0925f0a822b2b7460648e874357e1ecf0ab75caea56c78189d936349
+size 13329
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index 7701a371c3..61448871c3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0bb88c64bc68b10b1fb709135f445de5a5e4d78623448d0fef97504e025d5f6d
-size 24551
+oid sha256:caa94ed925e37306cc9453a5ade0266a897dc010cdde86b92a9e1d4710039edc
+size 22184
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c405ee9fea
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6d9458392de2e0f291f752a8cd9558c9eb891d4140e07c5c61a42a16a3c77838
+size 24507
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 363280c1fa..b1ae254500 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:52a5e65b23072ccfdc7220daca437e93342c04cee91cd971ba8d13872968ae7d
-size 36962
+oid sha256:c9fb80a007891ceba86d3cdf7711ba3a8d585358fea9558a0c451f67de58b94b
+size 37346
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 232ce0d49c..9f053deea5 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ea4a9e4b5488e925652ff747997dcbb58c55dd126c7b81f5222c30291270d7e6
-size 39147
+oid sha256:c9b3fcd78615bb01e7d6c044519a835222a5791547aa0c99a6bb7dc0a0ec72d4
+size 39687
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 79c5d3efd4..73636beab6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7c60ca1b2273df93b927550e51d69f2b3a2b59f5060e0794b1cc7dbaa432dc51
-size 36879
+oid sha256:12ce43111318a2d77ea2dd878afb2dc8f668bb976b2a7367b732a0a5c86e71a3
+size 37168
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 3444da12ca..1077e0866b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a47d3a323db2c60d2825eccbc28b2df21ff7c5f2f2da86e27c61059493b18f70
-size 38074
+oid sha256:d7702e2ac4263ad23d209bce8ad6fce2a7ba3f4ca20f47c7585ff7e6e0965eed
+size 38169
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 79c5d3efd4..73636beab6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7c60ca1b2273df93b927550e51d69f2b3a2b59f5060e0794b1cc7dbaa432dc51
-size 36879
+oid sha256:12ce43111318a2d77ea2dd878afb2dc8f668bb976b2a7367b732a0a5c86e71a3
+size 37168
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index e9acc8b13b..10967a68ff 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4c26b06e09a81b2990a7505b70a3bf2647e508d631e9fc65ee0a37364c685808
-size 38928
+oid sha256:7f133c65e9857599199920927e526d1c3a89b6eea0bbae6d3951f858de11530b
+size 39119
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index b79bfe022e..01c9056835 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:baa415403a4ecac05895381608a24844b5c6f90c561f561668c55fe821740cb8
-size 40193
+oid sha256:ad6ab5e91e5ef1ff565dda6e36c51bcd9458dba1aa99624afb63fea4f0f7f1d7
+size 40259
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index e9acc8b13b..10967a68ff 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4c26b06e09a81b2990a7505b70a3bf2647e508d631e9fc65ee0a37364c685808
-size 38928
+oid sha256:7f133c65e9857599199920927e526d1c3a89b6eea0bbae6d3951f858de11530b
+size 39119
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 6b5e4c405f..d3dc127f68 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8
-size 148762
+oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9
+size 148087
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 151e09cfc0..8227daecf3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e8edd6d72db9efaaed76ac64f9882a5b66ac30747355815955157a3f3fc98c2c
-size 149344
+oid sha256:47f20f3dc4e4bdbb6641f26b6e03d0dba0f2c7e0fe22fce927870d14f1472066
+size 148796
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 847b4e1273..b729e6d346 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2cb81f2228ad63ff38bb6abe44c305adc66378bf85d36264219427be58ac58e3
-size 63343
+oid sha256:71b2a1c6dba0b373e2abfe77e8327ac2e95c7286eeb53f83e5d802d10bbff96f
+size 64975
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 6b5e4c405f..d3dc127f68 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8
-size 148762
+oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9
+size 148087
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index 822450c8af..56757c755b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:85222447b700315e6eea458fc72d61fb741018cb80c3a4530d4efc08ac9335ac
-size 129373
+oid sha256:74b7fa3ce27045b0349950e976c424a155ca5aef680a58789877cdf7a7e9a32d
+size 128877
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 6b5e4c405f..64c7da001e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8
-size 148762
+oid sha256:cafa0bf978a59ff5903429bcb605afd2cb347bb64c3d58e749b0dd7ddfca6199
+size 148827
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 151e09cfc0..dd99c63f05 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e8edd6d72db9efaaed76ac64f9882a5b66ac30747355815955157a3f3fc98c2c
-size 149344
+oid sha256:a19c6af3352ad430c6502ccc9dac1ae8b4b8d690c810ab424c61208fa31e52b0
+size 149411
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 13ea2accd4..8a7a211694 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d48708d932ca9111f74d75654663e66a5eba1b419130ca3f00f0d6f997f119b9
-size 64166
+oid sha256:852b7b5515cad1bb129273ba95fc528f9cc67bf4dde0995276a96521c4399cbe
+size 66851
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 6b5e4c405f..64c7da001e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8
-size 148762
+oid sha256:cafa0bf978a59ff5903429bcb605afd2cb347bb64c3d58e749b0dd7ddfca6199
+size 148827
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index 822450c8af..56757c755b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:85222447b700315e6eea458fc72d61fb741018cb80c3a4530d4efc08ac9335ac
-size 129373
+oid sha256:74b7fa3ce27045b0349950e976c424a155ca5aef680a58789877cdf7a7e9a32d
+size 128877
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png
index 40cbe16de5..a72d4958d8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e635091747a867b0acbaac27225bb4a5c6e77a3b63005a03e80244425bffc839
-size 40314
+oid sha256:62e7ab953183e59348f5229dd398c6ffeeb7b60bffec9197139c58cfb335825c
+size 38257
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png
index cdb2102b99..b755ac33c2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0b68e89219ad178eb4a4854d8a0800975628b098aea016ea74d3f9e3e6790363
-size 46990
+oid sha256:3d4e87ee1dceb9c0d9c464d3db5433e849eaafde2e6241ea00a72f7abc5ba971
+size 44852
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png
index f9c5109ceb..0fce5968de 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e456ca95ee33cf14cac839b2b57879d17ca47b156da65c8a870882a90ef2c84c
-size 40664
+oid sha256:f54a07228afae0a4e03f824438fef9ac498551adc4740aca8bce0f79d7b5085e
+size 38626
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png
index b0937d3b09..542d0d8682 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4bcce9d53c7c094698f07de6c6a25be2c7831707581861df4b0cdf0c3d6d1fd
-size 40906
+oid sha256:cabf78c9278e2edcca8dac55f9bae26b38bf7a7be12e2ee5094922c2d0edc113
+size 38876
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png
index e561c5d902..16ce32c2bd 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:849d063e18b29fd54021cfd0db6088221e53179563d1c044d7a10510cfa717ee
-size 42158
+oid sha256:08d92ec4975dc62fe3b5bd332128da4b59b9f0c28bc797bb40df708ceb03fe5d
+size 40325
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png
index 34754d19d9..361f1efce8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0321155131e845672b07cdbec0820892acd0728770befafa07dac888a4a44fc1
-size 38836
+oid sha256:2d1b47d5457264255a631e07bb8c109f1e4a90a50b2159d9bda42e8b94fb7ed6
+size 36919
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png
index 3f28eedfa0..744c2d05e6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a51de9335637898863151a3a3ffa81da9a0743189c854d11ad666d81d9f2b2f8
-size 45228
+oid sha256:3d61ec70383611690046153defdaf1580ab156a97e789d4dd7db76e30f0fd29b
+size 43129
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png
index 410fe4da24..19cc8a54f0 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:66a7d55b40a9d5d7bc02f531bd3c80a1ba96e24a619c123f68549df355019558
-size 38989
+oid sha256:e7a4e6302b585ad904ebb029afa0c835383c2fa75e43e26cb3a1712caa150371
+size 37083
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png
index 6cc64a9ea3..3fa174aa4b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9bb3eab588d8cc20eb2bcbbcb8a7acdf6431299bf575a124ed7aff8a4fe6cd15
-size 39182
+oid sha256:5aad5fda07e4add870d0e1835d136b54fa459bfc1c113b9d36b1564d21c2a15b
+size 37252
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png
index 5107c6fc5f..0a2c54c087 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:aa5d6c7d504ab6436cfef4787a955422d9cc6b7c652ee70afedd0c6ca776d50a
-size 40651
+oid sha256:e846c3d8ab9c2fffadf60cfc6ad9d959f50bcfe14c6006c3c303c5f8171430a3
+size 38646
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index a3c7eda016..f4eb37f23a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a8e33e4ccb77ad7b3e317ee9b70360de9b0151f7391671455f0014a1a290974f
-size 395734
+oid sha256:285570b82885f1ae522c8ae79f6802028b53e76ebc08da4ee29bccc648001935
+size 396039
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index d0e5516377..260f750e5c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3b57f3a4ca9ac352fe3669a9388c85e25e91e0d54f309e9ca4f49fcb79b19527
-size 16083
+oid sha256:e8ea95bde8ba6ed5a069b2a6de2231267b6ea9a4e5d5c4a6efac166e0c563cff
+size 16388
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 8840b9892a..62b6486890 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3a968039c2fa94e89c75e395bab808d2960c37c43576b558438ec17bcd17e507
-size 184797
+oid sha256:3ce8ff927a9c9e414e2a37da8296b5a880a043d2e162f7169d58161c209adcae
+size 185108
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 7b19571de0..8392cf201a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:07da432732e812eaa091563610a3a0b490ca7f829260a5b61b4412d6c1875c8b
-size 99982
+oid sha256:3da137c4945b32fbebc70f4adb3bb2944d2f8d63aa0d30a1b10c52a9e8c233f5
+size 104677
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 749c81631f..80f87a5187 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8423bf9617b4834c42a0f0783d18c41b695cbb6e7716230a1f940b99a7b0efd9
-size 13283
+oid sha256:c479bc8fd2ba3cf33cc7a531fe31cea97df4939101f3b1f4cc200a29fa2fd2d4
+size 13736
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 735c9f5e32..9a1d495247 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4990c37241c349490619538b909e62bd76f8c49ffc3f9b7a2cf8fcaf9866bc08
-size 12852
+oid sha256:1de437e6f10abe8d703ee0596e625aa63c8f93d60c36fa7f30de9fbc53dcc60d
+size 13292
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 9685f02b12..0c4058c10e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:911217e791dc46e76b4866efc7eed59dc721795b0434f0a985078a9847347980
-size 26652
+oid sha256:a0c631199a337c8e057c2ec1c460bfb3fe2d0e078fc70d13fee96e057edc33b3
+size 26909
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index b1f7c53c54..c065adbebf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c1ccba126f5fef03ee24c6089088b2014911452a7c54eeab3caa49389b9859d3
-size 26253
+oid sha256:d170e350cce0f19d5635e59124a476cef4b4e58e723c13518f39249ca2144428
+size 26513
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index 882791b675..62789f5fd4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:093b9faa97e65ad0108882964e8e8af7a980a2fdbde32d8ff83d8420cf8b6f95
-size 26425
+oid sha256:9077659f30e368e2e5f6cb78ccd1b04c03df01c14d2e94e3c4dace8554b0d2a4
+size 26502
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index 882791b675..62789f5fd4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:093b9faa97e65ad0108882964e8e8af7a980a2fdbde32d8ff83d8420cf8b6f95
-size 26425
+oid sha256:9077659f30e368e2e5f6cb78ccd1b04c03df01c14d2e94e3c4dace8554b0d2a4
+size 26502
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
index 882791b675..62789f5fd4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:093b9faa97e65ad0108882964e8e8af7a980a2fdbde32d8ff83d8420cf8b6f95
-size 26425
+oid sha256:9077659f30e368e2e5f6cb78ccd1b04c03df01c14d2e94e3c4dace8554b0d2a4
+size 26502
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 1835595e41..806a6406be 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3c196974276b6e6e665140f5e45529d529ffa53b91ae714bbd24921e9a81c838
-size 14418
+oid sha256:7171960e43482db855e1f997d19604ca84958dd57caf35c39d8d34bd4dd803a9
+size 14791
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 01deacb825..6ed27f37fb 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1e9241a489f3d2fb93ce0b9678608418be5857a09e56b5726d1912599cc6e712
-size 13810
+oid sha256:860b7040447ebba7fcefbdf7ed33db2de3d756ca9f67000b845d547d443cd23e
+size 14185
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 80f1c30896..564db946e2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:162b68e63538b71e1b3a265281bebd7f56df3cd624caeafa94ab80d46326e2d4
-size 28162
+oid sha256:f59f7e98b77c9e4c82e1a10c24a439733bab9ba26472aa7ca13da8bb4f4052bf
+size 28694
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 099554b6c0..edbd888d35 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:775effbea7088ea32b6eef036968d0be99bc267d4fdea2269d3ab1101f0ee240
-size 27542
+oid sha256:69cd17722c03402809b2a85ae18c02e3e7f934dd6526f18a40c64654a51d8d99
+size 28092
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index 739e84de45..91573e4536 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:95313cb1717aabbb275b4ed039741da2ddf95af02d177f426b354701cc9badd9
-size 27947
+oid sha256:9d322e159a4c5e39a1bdce0628093bdc9ae372ccfd9d1fa2088a3e3094a09048
+size 28096
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
index 739e84de45..91573e4536 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:95313cb1717aabbb275b4ed039741da2ddf95af02d177f426b354701cc9badd9
-size 27947
+oid sha256:9d322e159a4c5e39a1bdce0628093bdc9ae372ccfd9d1fa2088a3e3094a09048
+size 28096
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png
index 739e84de45..91573e4536 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:95313cb1717aabbb275b4ed039741da2ddf95af02d177f426b354701cc9badd9
-size 27947
+oid sha256:9d322e159a4c5e39a1bdce0628093bdc9ae372ccfd9d1fa2088a3e3094a09048
+size 28096
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 93e9b663f7..0c6de663a2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e3b5474c29a881c61032396785a5c62732fc06f1e551fd6fae8ab620782774ce
-size 395492
+oid sha256:5a55394e59395b36f7a1e964bdce7d9f7d5f33252c463d0de7a87c213117231a
+size 395169
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index bbc32091b8..f644a8c108 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cde41cd981bd9ed350b6c4439cfda793946e2b7c14ea701af8fd5bb528329661
-size 395494
+oid sha256:44acd9adb83a35d38a9da31f8055988740d238e459d7e77145c5c2ad53725ad6
+size 395169
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 93e9b663f7..0c6de663a2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e3b5474c29a881c61032396785a5c62732fc06f1e551fd6fae8ab620782774ce
-size 395492
+oid sha256:5a55394e59395b36f7a1e964bdce7d9f7d5f33252c463d0de7a87c213117231a
+size 395169
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 3d12f9a3f6..e2663fb42c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:77983bf8dfa0683d472683cdb7ad545beb6b05fdfb8b82ecf90db49d387db72e
-size 395299
+oid sha256:e4e9e0b3c5be9a07af8cd9a4f4ae725505148805327455350d9504a5974032c2
+size 395377
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index 1b77de0157..d16aed691d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9a28b7439185193fca0f389d36f287fa38d29d18bfc046a125f936026ffd1918
-size 6318
+oid sha256:9225a19843eb5c9e1a78a7bd307958c2cad544c58ca2637224455326dfcfffb6
+size 6315
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
index c6afff3c8f..890541aa37 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5e11d4107dc8894408eac31625b4061a59e2b5eb955229d4abc64ebb1191cdc9
-size 15614
+oid sha256:78bb8fcf8275cd3568f6f254c4d7ef1a4e7d251162c8b21bf2c85baaf2ab6c8d
+size 15257
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
index 2fff84f3a3..fcfe80f7b0 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:459ff294007ea6238f87954e04e8e13614b7c5fa7ade01f44670014013ebfead
-size 15382
+oid sha256:628072ca45ae0d215ba9bbe965b68442153d81ad98f330ab1178a0f8fcff3e24
+size 15339
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png
index 8aea9ffda4..82352056a3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a1805563eccf45f507152722511cacfa3f0411d1ccb90dd8d539f9a4a697b56f
-size 14459
+oid sha256:43d8d24a65ab1174f6e262563b898d3168ce1e38a1af8e53b1a5a8af205f70f5
+size 14103
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png
index 48767dad09..d3a5dd72ac 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b2824676afbff473eff8c8cfbcf8b3e58b2851e37324c6a8d9ba47d626b0f8bc
-size 14234
+oid sha256:93ca4c1155010d01315b00bb79d9807cf0e6f80a609be668cb5600c16e82ef4d
+size 14190
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 5b3f3b3f31..55b466482a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9034720950ce90da57f7fb3b811dddf95cb5f621a917b2fb1e930fb8aecac02c
-size 44089
+oid sha256:93fc8ccac0599b3ad6e16b4cf706d53f7aa51c41b50afdb64eb66992c338c65c
+size 44392
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 3ec76db242..ddcffd970b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cd04b0d19c47008bec4e2a458647bbf4f0e4157452d380db65210e3a7ebeed3e
-size 45123
+oid sha256:7d4dab3b14b9ef45c243621f4253f177623103b539df82166223291baf6622cd
+size 45080
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index b600b0e223..35f6114312 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f7b4b03c456266fbc7b476f4340551a9acaf731678af59961bb0890032c8cc53
-size 44669
+oid sha256:1504172c389945b60733645f3166bd6053ad5af62aab9ca31da6e4089d27eb38
+size 44636
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index f0d1df632d..f8198ed1a5 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5a3b32af711ee8de6e2efbef25c1b747883807f341a67f2fad5c343be89b3bcc
-size 42863
+oid sha256:afa74a13826fdfdb795c705ae537cf9cef516ebc2ff84a0f4e1d4bcf413ceada
+size 43221
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index 682e400eb9..381ab955b3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:82183d73c4ec193b2833ef5a007facd98d61293864532a200799f1ed75291002
-size 35550
+oid sha256:430ad21d6faa861bda2a774b8e58891beced9a7a442a4518455f06c2cf6ea11f
+size 35061
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 325e083ec6..bb7fd6311b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:08465420dfb59d2fa6895106ed649787659e08898efdfac5957d70c6e36d64ae
-size 45770
+oid sha256:1b5737eab1418e738ef66397e5a2d2c8ee70369d1a84dd59ff35e7fbf4d3b520
+size 45979
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 1206c45222..ca9291e476 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:07e4c2347902204df2860f5ff48412c31b9904e61d2e3e481806ceb79b2f8d2a
-size 47550
+oid sha256:1a58a45c850eb9f3095dff489b306c250484124d399421c9afece6d7061be253
+size 47472
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 1e8fe02f36..1b643ab7d8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c31855849005a3bf4f5eb02b8461cc2d26ba84637115d7e8390aa0732caf6013
-size 47013
+oid sha256:9452d0ee6d79778004e4b68a089d0105d4f87a2de4d89c36b889c73c9087c96c
+size 46934
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 8a5d3932f4..fe16c0bce6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2df48ac9b873b317c7cb1163bb96d2d0fac6f6c7e00747ec8e10e0e7a22d8cc4
-size 45129
+oid sha256:a9ce8f73922b6df91f3383f3d27464a2951595da9c63fbdbf8c0f87efd0d4115
+size 45445
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index efd9526466..626414fc5b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:48738c3a3a6d9caee6590fb756cef164b58d3238fe0d86fb4cf8acfac627e71f
-size 36913
+oid sha256:1e48ec2f2e8a992be7dd3c12ae3780d3bb110ee66758f72586d4f3aee39d18e8
+size 36935
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_2,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_2,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_2,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_0,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_0,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_0,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_1,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_1,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_1,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..05cf88f8f9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1978bafb5fd079de3fe7945a0574b2b19b0480d9d7d146e034b749e674f76254
+size 48667
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..05cf88f8f9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1978bafb5fd079de3fe7945a0574b2b19b0480d9d7d146e034b749e674f76254
+size 48667
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c0d3cd8739
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df79a0bd1caa8ecf4b701eaa589cc90efc748b986388f9ae5636b9dceba4492c
+size 45928
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c0d3cd8739
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df79a0bd1caa8ecf4b701eaa589cc90efc748b986388f9ae5636b9dceba4492c
+size 45928
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-14_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-14_15_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c63cf7083a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-14_15_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e63f683ef130d77b55966e954a7cbc144dceb5960056bcd01d7c0c9583b3b03
+size 25358
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-14_16_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-14_16_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..495f0b09a0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-14_16_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5fd8aeb2e875a1280a70ba913dd913759af66fb4300ed69c7e109ec8e8378a09
+size 25051
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-15_16_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-10_11_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-15_16_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-15_17_null,NEXUS_5,1.0,en].png
similarity index 100%
rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-10_12_null,NEXUS_5,1.0,en].png
rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-15_17_null,NEXUS_5,1.0,en].png
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png
index e10b00e85b..d6abcd7838 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dedb3940216fd18804157f708340a535008cc753a579ced3ac4153f99bf01218
-size 175737
+oid sha256:c5650ec2689d4ce0cf0785e12d5cb899c1bdfdad14c1f75106be1068df662f53
+size 188815
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png
index e3804f7073..5898fc6fa7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:19d8a7e0d477cc7db1bea85407b71516347b158777db2e3889734f2129b69de3
-size 175762
+oid sha256:2e5e11d9c1e7d4b950a0d080e8104f08b26fac56332d243502430dbfdd02c60b
+size 188259
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..91af1135e8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1763f6e5114d406d9e2a5cbad96113d5d36f6ccfdee1cfade0146c1804d0a725
+size 5982
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..25592c4b2f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0582cf0802ca4a1e5fc823518f0dee9fbf31b3b2803c0a89baab35885139805e
+size 5917
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index d732d1c37b..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:724bc9b0db8f581119201a8db3c405c9a7e7261cccea45a354fd37a5e33fcb41
-size 11141
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..08f201da01
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b009dd473a3a2d5eafae382f2477728cb7ef3053c52ec51bc3f3891769ae5f57
+size 10264
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 9b08510703..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:109a1a9c6359a9fd7fe4258f022e845c9b8142f9e590a03e5c2efa9273cb572f
-size 10742
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1f7c631359
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:153f8eb0807c8b34d2e990819e8f892382aae1125350d371d08a35454423a727
+size 9948
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png
index 3fc8c841f2..e418577f55 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:38c6d3f4a47ed89c3deb4150024b49c0a533a859f21a1592a82309b8c7316ea4
-size 152242
+oid sha256:5bb246e6f180d6eef61c3dc0f884a070761d03b4f396d2ead1c178507a6bc70d
+size 151279
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png
index b204098bdc..f9e018c372 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f82840398e396e038ad68a5fe855394a0088c413e7aade339998b8ed63fb1c09
-size 157273
+oid sha256:2905961ddc979076374d5cd7fb0885b3e3fb7d13a3fa0ae915553d8bb0174c65
+size 156313
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png
index d389368d7e..f7bd5a3551 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7c5ef7519aef3badeda3983b2ec5474f31404bc06b4b46b9829848dc7884fe1d
-size 81749
+oid sha256:35c0b796ce83fbe14b477f472344e4b3969033b77460a8744ccdb2a3235bec03
+size 81090
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png
index 722a25f340..39a0c59701 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c9e9b769422e4714e87ace97058e5a8f064e3b927e3a7c6042494de8381d6b09
-size 85856
+oid sha256:26b0a776582b02bf1ccdc7a75720097f9f653635ff14c42f432aadda4e832d61
+size 85152
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png
index f2add007e5..496a355edc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4970830b042e7b6588c03bf632ae3ade5b39de7339e27618ee341cbd9861c0e9
-size 129351
+oid sha256:2c5f983843e2d0689de6316091ccecbe4442b377427c8a5d9edf6a76e4f1d885
+size 127276
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png
index a048edf7d8..46037460cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4aba48e03e8424e5ff131c22fd5a606280745dd318f5822e2167f3b39794ff41
-size 134569
+oid sha256:b412cc33626f9987b35549c1cd693af7e16c79179093a74b7b38da5964da19c8
+size 132594
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..e9c109b99c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6c6a8e9a5fb8dac5bbcd7e0c0ac9df56759dc2e94a5d5195dd9102aba0c18617
+size 26509
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..555d89e509
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79997cff1c86462be9b743f1c561d3b8667570b474e0187352d7d2e2cd056888
+size 26107
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index c838261a0d..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:dfccebdef9523a984d9edc2414aa159ed025aec14f891945abed73472e462df8
-size 13641
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 001fbca040..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9c971ed744305a474f182bf3d6fde281b9034f99cee4f4207460a733ac8490a7
-size 13464
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 53c52d9a7d..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:999475ddc51f74a590e5ef2810ab31222127e55cba01fadbeb8cb8e9058ad6f0
-size 27170
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index cbed0a0752..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6fb7202b53005274d98f0aee8014326f02bb62fcd323b38b4d8a447078499dd8
-size 26965
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2ddf5837cb
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:064001a5d6bdf0c3c9c160e95c137789d874e8a595d3f30def3d7d77a049d4f9
+size 12113
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..42e074b9b7
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:36e2d799245e69505f55bfae68401570d983deb7747bbdfdd5f01372003587b3
+size 11994
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2ebedc0722
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4efea594700a66b3a8aa2fdf95d4eafd937fe3adce0c70982e3cf713aa662739
+size 25743
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..924713cef0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:76d8e1608b83151e5f444669713808d48db77666b33b5ff44c8ee94b27b6bbca
+size 25465
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..33128653fe
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8bbfc46af47025d2e2e22839b939f32b3fdfde904a7a08edcc4cfd270d20b9bf
+size 25750
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6d5ff5d621
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c5ed3ac2e9d4f7c1090677a49ac25bf997ea09d53423ccbdd6b6ab330cf30098
+size 25518
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 119678a386..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:82550de45d39c52f34fca45f84b6e9eff53dfef6d2e9622dcaf49c707927d665
-size 7873
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ba0bb563bd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d75da9638e70268c303f7f1f8affa9302e449d00edef0f21df5155be3ce3ff3e
+size 7646
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 896fd1051e..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fc39041ac035aa372640ca7b2e9af254564aa10ce7a7f4c55fbc4bca9f6dc6d9
-size 7844
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..396e287a9d
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc554dfec200c02710c7ef21be0cb22753b4457e96738d3a74500617d8854e41
+size 7656
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png
index 7c99f5e856..7a219f8773 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:54c12970e3563de958f88e4c538dd368f9810266060627393256a91741f7c6cf
-size 53340
+oid sha256:efe8b8964a4588512338873afcdad87ef9d6d1983032997c38e2f74c5cbd279b
+size 52046
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png
index def9cbe0d1..a66dc2643a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3b7ae3084cb9d1ecee2e4db49c228516bdd7352683e797edf737c8d216922dec
-size 65601
+oid sha256:44135b12caaa2fd0bb814a311e54ccd899c302c10daa4bce09330815ac7fef77
+size 64155
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png
index 258ce3f3de..3665a2f820 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dd034d439c08793e0dfd59f6bd5dcd88c06ded6cbe98172bf3cf296888e6d575
-size 51244
+oid sha256:aa580473120654d5e49e1386c03bc2f813f588af7eb9962c45b9896f6b7b7124
+size 50003
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png
index b2f16a9d23..f03ba74880 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d9d835cb1a420117b4d967181e2ca0fad71ba243d6a17bea08b82cae41f6b8e2
-size 68760
+oid sha256:aa77a7edb5d8194dc7cbfff08d86f3d4b86ceaa900e3391c0d20258a39da95fa
+size 67333
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png
index 3bbd94a1c8..35f86e9a4a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4ad09278ae2ebb8171adba96d9f6e91d0cc4f120b0b2368087796dadb37eeb87
-size 58539
+oid sha256:5122e480cb7c386427961e1e262dbee7e75b37939a7430235ed6e1d0c47cf544
+size 57260
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png
index 1c32b14777..b9b4cf2174 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:145856c3a7ff43702403ee5b86a7119b0475f03fdbc0f2e7f84e10350a64b150
-size 229842
+oid sha256:21b5e874428ef5912442d3a92ed694f0f016c3236781a02886c5c3a589bbb317
+size 229385
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png
index 53f57d95d2..450b33d8c2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c1f0939b0c22ab89466953889e5bb63e11c45f02af79eeba3f377409af61d356
-size 230809
+oid sha256:0883699f8e551930478d0e43540497bea6f1cd9fe6f2b95df5c5e36370eb3a9a
+size 230368
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png
index 4c991e3c30..86f0aa481a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:082206122d4e6d9e6171b3b2444c576ff7bd47fb3946d8d98e2812654f39cd40
-size 73641
+oid sha256:dae5b99b3234fe999673593dfe75feb44b0122e872af62dd2bed0db610132655
+size 72595
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png
index 80eeb03c19..fffa0ebe9c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dc695bfc589e8e5a852eeb9b2828ce968d17519bb5be957cf2734a7d6d9cc356
-size 89482
+oid sha256:74880d4c54a6ec9eff418ffdaa1da5888cc1b1ce4d92f958c36c36c7947a6321
+size 88190
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png
index 727cfa4b3c..d49e704e4b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9efb4310c2eed085f8f72be66d725c628e07373cd18c262c18be510d7b042f69
-size 393373
+oid sha256:a71e1d72dd7af142b2a8bb09810a416f43f619c697ed9c0c1cdf3d85df0259f5
+size 392639
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png
index 9c29642146..0290995c54 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:19982d091ffcb15a422e75461adb3d039775645eaf74b6bfd4a3ee617dd4555f
-size 347932
+oid sha256:52d83b9001db80db98e0a837672f218a77736e65549f5e31466f8aecadbfff54
+size 347295
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png
index ef081b76b8..1f8e834b83 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9a243d53d10ca2eaa249b22af8a9fddf1a8ccf60db4f3ad9374e31cf494fe878
-size 54909
+oid sha256:79a36eab1ad6523438954ff55aa21ac92cd349f119e8eab4b61d25b4c44a56b1
+size 53627
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png
index 0b13a93b0c..585b1e3980 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3fdd478be89b47fcaf9725ca14c1e775087f1ccf2a266f3476a537bbc2b29922
-size 67476
+oid sha256:f7b35322f9f344fb49ab2e2642a28c1c04bfd960a0354b4382240886616658bc
+size 66053
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png
index 9f5b7d48c6..d52711f7e7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1a3b5bbcdd1593e81384b335045bdcb0b3e01782868993e9f6437a15ca39dbca
-size 51380
+oid sha256:3e6daf18be28261abd6753b39faa6f7bfc0b820d9e42a1bb0188cb4cc8ced6b4
+size 50224
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png
index ba9f8a8d63..ac85571fb0 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7e26db0a0a8d767d6e732c1d2742cba3c3475d761b0c3017ff01cee7e3d362a5
-size 62773
+oid sha256:5146396bf9b765169c583b14da54b43d55b2d306cf35890d500fde90be3fd36b
+size 61348
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png
index 7d12d81b18..4a9590eb47 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3d5cae7d73178aecc12fbf5f1f27c9e66608b90d462deec862a881b403469e93
-size 49475
+oid sha256:28d13b05def8bc769e61a5814b6c96afe704c773d1d86f6d11b2021ebce81035
+size 48407
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png
index 44fd92b664..06b5d396e4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fe9456f4446104142221e28167a578a2b5cac772dc35aeb14352c0d422dd6fb0
-size 65726
+oid sha256:2d23ccbe1a72f6b375cd1eca980127ada3b8aa39aeb8b29e2a7caf80aea76165
+size 64255
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png
index ee3a9ae3fe..289c358d9e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cad3b5ce023d890fd4a5ff0f5bbabb678bc6d5c76d3476e8053673c06f360c8b
-size 56102
+oid sha256:9adc9689e6e88eea0fa467713bcf8941ca645219ce6e6f3cf4dd215c3d1301de
+size 54965
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png
index 8e578cb1f9..6bd18e0e57 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:71b3c712fb8e4bca178afed2de6f4184bb717e3415622e97f14bb4fe36aaf9d5
-size 229097
+oid sha256:c471afa95f769bc06f52ccead68e7f50db5f6be436380e43b47c564b299fc2ee
+size 228623
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png
index 13d92ee329..a841399bab 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f44d772e5b9fe65a79e05aaaa82536b19382f9d821c0412c47a7d330d539ffd2
-size 230080
+oid sha256:be72eda04000150aefc4570d68015d21e7647f448bbcd69ea2d75e62ec0b4caf
+size 229617
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png
index 4a58253b47..3eca13fcfa 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:41a6c2dd81698696708802276b615a78ff22cc7cb8ae2d4b8d8cc862bfe44a24
-size 70856
+oid sha256:c14449fed7d0364077dc8c5804064dccb7b2969595c4c58e8da07fe03ccbe1bc
+size 69731
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png
index fdb8345d8c..6ebc0f4cd2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:167f8368e0b94aa05e5d1cc30055e3b0c2c910752d719092ef2d34aae09dc832
-size 84649
+oid sha256:5e29e1504333aa44eae6c3eb4af64c337fce01af895c7b920781ca895e6ea5a8
+size 83402
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png
index 2a6187d9a3..f8c0aa4bd2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6ee754e789926bfd12212a21b6ff882ffca337d6903125ba5e1d7cd0c1c218ec
-size 189364
+oid sha256:9045218a096f1104f1672f733fd0cfa21114dbc41d11062d4418febadb659c54
+size 189065
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png
index a65dbca59c..5f16ee28ee 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:29f9ba8c19287b037f67aec5af4469174eb878b7ed5baf222f66e159faee6e16
-size 178410
+oid sha256:17afd91f19020067c3ef1b739834222e208d378e0a51cf37aa31063e187c7244
+size 178149
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png
index 08c13f8f2a..b5dbff5ef8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:789ee5ccad8356198cdc0634b4e9a65ed44be2d26e7ce83a8662598c1bd8d4c2
-size 52765
+oid sha256:45974a6f6a441f1214bd557c218877bc756ad49b4bd3e03313915ef9cfb9ac3c
+size 51578
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png
index 300bcf7167..f04e4afe02 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7b40a2e5d60a906d7c35c3ece1854671f5b319b00ad077322d094cbf906c07f7
-size 64803
+oid sha256:713006427939a992312a0d7023afe3c81810caa5b879792c92ee89ee93881113
+size 63357
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 101f913aa8..791a623ef1 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7c724bc77185a9ceec2cf092cb1d7865b13718d5320bd7ac4850bb85590f05b2
-size 52294
+oid sha256:3959ae6151f02d7f41afe498b57a9b55255ea88335504d9d17750259100db1bc
+size 51232
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index cb28314caa..633b9ae8b1 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a303894134ed06348b609e41cf109dcafcd994c3dffabc6d9ab436fe92605245
-size 53710
+oid sha256:432f2a5451368679c076515406808f1b136b3db8239841b7ffa65417c3d9b901
+size 52632
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 2ba437739f..a8162ad980 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7523ec0d6defd7074af0c804fdde64fb8d421f6cfa729bc1c4f9858bd87c42d0
-size 52554
+oid sha256:610aeadaa817fd4e50471fda96fe24aceb48ddb03a0e58d0305abdb96553e8ec
+size 51463
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 73715a33a2..b36175d6f2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1f48089147f7e089abfa64254143a80857ccc9f840aa60403e1aabc67e2b6d51
-size 55458
+oid sha256:ca313b8e0c24369bee836649c7984ac18783893211df46f792e36261bbac0bc5
+size 54170
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index 9ce6f92309..cffa085a04 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7ff682ee8363d450bb76db72ea06deea87fa47692ce319b7dff315d2a10dfb6a
-size 51033
+oid sha256:9272791f4ed5afde353ea893988fd1140bb6b090f19901c0bbfb25ba37e07e52
+size 51100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5bb2141eb2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:51320b6491f229241d6ed52aaab871ea217d32ebe42ac42496bd702543ee45ff
+size 48800
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 57eeca6786..5a207ffda8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e84edf8adf1a89153dd45272d36e04561d66f2ea765ad9edecfd8d750ba99f97
-size 54237
+oid sha256:2d1da7016a99ce5c913f1f0c3c7c8047531af13412b846685683160212e88ef5
+size 53061
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 8baf81c8b1..4583dd5346 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0bc9521bd1576d47ca6f643adf43ce3638d40328207d908ec863c72503d34f24
-size 55682
+oid sha256:962cebd43ee1f0872c25413d8a62c9dd279751e00b9c2154f156e49fd8c25ebd
+size 54554
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 40900d7e92..2d30ca5d01 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c2209c3cc4e7de32ed92b3b0da4616b54d19091d43d21c0e4013728450d0d3f7
-size 54595
+oid sha256:50b8c157da1134708777ce4940c58f50a48414e837762064059d44082a0303f7
+size 53415
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 5777c7f77e..7d4f85fbe9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d7bdd0ca39534b31c9d421e28337712b1cf2aaf841a766fcc6bdaf996c756bfa
-size 57524
+oid sha256:e5ef4489d60dcb1ae6f976e229849af0c89dbeb7bcd4e37a92f18e2514387a1d
+size 56253
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index 2e2bfa89cb..d33576a149 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ba349f81d5c417c612cae1263504ea5b4e83dc606ec20c3942368e8992b87ad4
-size 52886
+oid sha256:4d6f69846650213dc6b218c97e2eb8319b5fccaa9451822b23c6c2cf9d46b339
+size 53653
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..5405001cce
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0a704d06f30f34414495bc6c74ac9a69bae32f62d0d9608c8ac43550c0cc7be
+size 50426
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png
index fd00dfae33..41503c31be 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9e0c73b8d86c064ce46ade6477cc91803d543199657e597a15a7e21bdacab7be
-size 6541
+oid sha256:774ece5436ab5a025a6a7134120069922276329bf87408a90263bb8c425a0568
+size 6510
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png
index 820b688066..b41bc75694 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3af4d7ce438da56851aa3041138558d8e57ac181775476b983646039a1cb62ae
-size 6602
+oid sha256:55aff072541c752abd0a8fe48c73b38b42ea47ab4779a2a3ae37a449f88ffd24
+size 6606
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png
index 636c99bead..232570b461 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:75bea68dfb5165f0f8c755237b627f5c61411fb2ff4d55a18d1daedf054d15ff
-size 338382
+oid sha256:56f53e2c13480d5f31d38c671612c81fa802e1f35a55ada99d544917f01b91ba
+size 338952
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png
index 14d6ab78da..45d008bb1b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:66e66c919bcc118c87c1d1095d03a105d42f3d630466f0a08d2cef011e67e9e4
-size 327542
+oid sha256:cfe3663daab250bcd733ab746e4864472691b40cf927a3cd73d15b323dec2b68
+size 329945
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png
index 5cadf29e4c..7349cf6808 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9c3c01fb242bd0be1dd894542b3980d359e9276a55afe019ba95423eb7eec7a2
-size 340958
+oid sha256:c449e6e4162f12a6be7faa668b930b294489ae217c2a7493f5671145a926079e
+size 340944
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png
index 68cacc0096..e6a8465877 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:595309f942c0ddc37965b8c1e2ef7afd2dc896929e71efd0696ad9214178c9ab
-size 322751
+oid sha256:0355120442254880360b982df6c2c7cce84a471f632436c96e79ffafed35de14
+size 324378
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png
index f32ba6cbaf..98974210b9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:69c933682865e48212cad79bb2194f2bb20a8133e7c76ac1f53f8691445e840d
-size 421976
+oid sha256:5d05151967955c961ca47619de2f28f86d0350299b61fbef0371c1706439a844
+size 421908
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png
index ebb9513813..28985b1af6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:afcb322f07f594e26b051146b22919daf35317c844669e6204c1f4d29f9fb7ad
-size 404245
+oid sha256:3a2056882e65afe8fc2d0dc60efc7912ca5674acb57e3690136a8704f9f063ed
+size 407942
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png
index 2c884eeef0..2622fa3931 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:88d2194ffc00644a2666175e86ca49dfbad8988fbb98346128d0add77c79b8eb
-size 421289
+oid sha256:6521d409bc1fc70fa422ffec400d9f20d0b7fe16adebb7f5b0a9e5f135328ffc
+size 422213
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png
index 5ce6c750e1..e28f44bb13 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2c3c17831149c3a6a5b058e8b557b357e84157cb6a0bc56b945c9e2dd3bdc0de
-size 393706
+oid sha256:d39cfdb0db12a6d14f3105aa168ea002a845d2083f07ecfabe0858739507018d
+size 396789
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..3b7a5b286f
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1fb1d9bb4ab01a0af8ee20c08084fd01baba0f77a4c4a39c179a8e19d1dadd47
+size 46624
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1244226217
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18749d3e3a8b4e1176fa6c8303439d07306c3518e6be625d5ec236e2f24433b5
+size 43265
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..05cf88f8f9
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1978bafb5fd079de3fe7945a0574b2b19b0480d9d7d146e034b749e674f76254
+size 48667
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c0d3cd8739
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df79a0bd1caa8ecf4b701eaa589cc90efc748b986388f9ae5636b9dceba4492c
+size 45928
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d5c1931df5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:239eaab2812e0e2968f52a41a741b8782b28ceaf72f029267de6248a5ce67fc6
+size 23063
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..560952376b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a9cf7435b528b3920e621d6bd688a8692c8bd366db1522d2947d5e204f905644
+size 21563
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..d5c1931df5
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:239eaab2812e0e2968f52a41a741b8782b28ceaf72f029267de6248a5ce67fc6
+size 23063
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..560952376b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a9cf7435b528b3920e621d6bd688a8692c8bd366db1522d2947d5e204f905644
+size 21563
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 923e05f0fc..9f41d45892 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ba708a48280a4f42196d61b0cfbb7f0bf6e4a818b8e76ba981e5cbacff37c9dd
-size 25493
+oid sha256:e8c4254a13c72f1e6e710caafb173623697b3aba5472ddbf14a9a8a8a16722d1
+size 25454
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 9358cd9f18..4839079fb2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8b75a4207e4dc3fc0e3a124574e0352eeb7e731558d90b4d19060d3a046b76e7
-size 26656
+oid sha256:bfa0258b099d0fb6451eddcf38dc2184bad611355f56054ede8c874ea28b9fe8
+size 26579
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 4fb8f691f4..b87cf23f84 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fb77df9e072715ed947537e4474d504b5b5d533bfe9cd888362a421fea5b53b5
-size 44068
+oid sha256:4a9a3902dd33ab3e528fa9c534302228113cc468914025766a21397ad3eda9e2
+size 45064
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 4fb8f691f4..b87cf23f84 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fb77df9e072715ed947537e4474d504b5b5d533bfe9cd888362a421fea5b53b5
-size 44068
+oid sha256:4a9a3902dd33ab3e528fa9c534302228113cc468914025766a21397ad3eda9e2
+size 45064
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index ff0fc9708e..30360f9f5e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:004a25de04aac1ca9cbbad77581e0b52a6f8fba30aa2e64c03a791c54b297d5a
-size 48875
+oid sha256:cd1cc710443af62efddf0ea165a6289912f471f669803255368d3266b872f5f6
+size 49829
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index ff0fc9708e..30360f9f5e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:004a25de04aac1ca9cbbad77581e0b52a6f8fba30aa2e64c03a791c54b297d5a
-size 48875
+oid sha256:cd1cc710443af62efddf0ea165a6289912f471f669803255368d3266b872f5f6
+size 49829
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png
index 2d5adfdf5c..1bc1883b8c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bf048607d3003004608786fe123bca039b5a6175aa52194ca54b4d54a69b7213
-size 24135
+oid sha256:27410489a8b575564662f64fe4149ebe08a9b9b3136f9a3047acca100616830e
+size 24618
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png
index d96d3a80f6..7419093421 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:57c119c7b2291b4a6431f9ed7805e74464a175e0a169f147ba77627b9174d2bc
-size 25074
+oid sha256:75507bd9d98b762bb67b732f47f6409476e4886239b2450df4d2e54309c52cf2
+size 26303
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png
index aa6c59c6f7..3524f4eaa1 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8415a958d4157f226fed8456fc0d004c1163d1ab9e24e57bfb2b369b4f7d0eb0
-size 26039
+oid sha256:e5ecc29cd0aaa41be1652d8b035951d287adf1e28bb9c825c99d79a5f8fbac34
+size 26661
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png
index 7b5233f7b2..893e73d603 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2de43d9a1e0652b2326f6b5462a915296388b61806ab2c8c62e4a7c15808a8a1
-size 27112
+oid sha256:c47014cbad6ac7446bc4dc1c21560f8a0b107fc8da25b8c85b89fb2591733efa
+size 28466
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 0a43709e00..4f09628e11 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:07e1283e5ac86cf9c77b0695a6622682e7257abce1e7787440f50c8f42ca0291
-size 64632
+oid sha256:67144695fd2efa7e81d910606e2ca4486a4365b54067fb4d9c1fb26303f86bfe
+size 65099
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index e26b93adf7..888cba3741 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dac098de353f8dce2d8eea77bccdcebf997c642f05afaa15c02a9bcf230fa37a
-size 200158
+oid sha256:a7f9a86df68cb0595c23c86e7b91f73af55531dd0495f0409471fb94165bdeb6
+size 200178
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index da4cf4461a..7da87c44da 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c4b2f7af207e8d2f951474575de12969e0fdf0831ce68fdfde5b67b364c67156
-size 55013
+oid sha256:647b616a0153b433399004436b2d7259b2e0c12a4cb621d87df4cb0d576e6fcf
+size 55422
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 5ebb85150a..2884bfee01 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:91fcd5dc74788d4f597dfc265a1d6a7511e5f4db47323d930f7c54c7df7d62bd
-size 67464
+oid sha256:abf293ca05d36da1de3f68d8b3d60b2da80c26dfb189698b949cd06c48239f73
+size 67839
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index b999544c27..6eb72a9709 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3583a0758b9c02c7e165dc593f7a090a54d70bcfa0f39853ba3bddb42850d404
-size 204354
+oid sha256:d10808329ac9280334c5bad3230eace88eb768a9e13b7d736c4e99cc74124c84
+size 204325
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 784d1dbe36..bb6eead4a7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3dac3ed1e6c46265e8b0f7bb479042271668178ff2ad34baae3cb2d777c7902a
-size 59091
+oid sha256:92b64a2dd7c5c268a12ac1ae154c5daeaeb267c0590852b3bb83793b4a01f631
+size 60229
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 1bbdba6045..6e81844720 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:07ce6ce83db08f8801f83dca8256c3fd133fc3501e237db6518d482f2248fc76
-size 29665
+oid sha256:99181f0a4a721479e0c2e2eaae91f4637a7a01054159c5b2b3c9173ebdf6a1dd
+size 29649
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index e10aecaa41..aa92897b18 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:378b3d99b8a5c4f30a6148af614526a376b02f30afe97f2d6a7c10c097d70593
-size 23386
+oid sha256:ac2fff68f6580426fd3273f084ec375123fabaab4a706e7cd153332d2947173a
+size 23369
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 9e1c8d9f20..883d55bf0b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e07de26f3432ed6b5a6b85490a52ea7324d11ce7722657770ba88c0ebba5f37d
-size 54138
+oid sha256:9e490123827e4275fd69bbc21990d8f10575da224432fb9aa3c93bcc51078bb0
+size 54114
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 3cad85cbf3..bfd46c52f3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:685460d930fee9e3c2f982b2245bce5c47e1a40570b5a69151cb3b12ad0cf2c0
-size 28315
+oid sha256:e8eec289c3e262da582a77de0229d818f061ae40818556461d0a31ac3065dc2f
+size 28650
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index fc0e472e08..a7210f34cc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d7ef8974e2d69d8ae325a3465088c93018312716636fa1624d5fdf66813aac4b
-size 27887
+oid sha256:fef45ba584f1edcc9ea0aa27c3e13678bbe3c834ff88a71a91ca19545b94b280
+size 28227
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index d42d35871e..e01891c06d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3d2d88ed1abec3c370b0794ad81647a4d6885545b57f171ee98907c59e8a689e
+oid sha256:d3c1fb883194c1137e4696b1fcf02b87aab42814f1e13f07383585159de615a7
size 28903
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
index 1ac04e5716..54dd5050d6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:90246e3319170b6dbad8c4a18db08f82c1d09a62856f6b8f457479b84e81061a
-size 25384
+oid sha256:ab9225695d5c1c7a42da36cd78378713e7056808de3dea01e2cef92e9972c64e
+size 24875
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 70780621e1..a5c3fe25c9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2ea01e575d960dc098a747b6f37891535e2a1028722d9923c92b81c97b99be3c
-size 30740
+oid sha256:5dda2d8f3a0fce6d47e25cbbd862a740d0f06c7b6c9ae9590210cf07ab840d5d
+size 30769
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index e5687588c6..20c39d6876 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:af817f9b053559a95b43bc40d4ad289f3fe72f28cbd6088f4bc6fceab52d2c48
-size 24201
+oid sha256:57048d99d5de2857fed428c8f9d99d6a59c9856c78a1c6d21967d12297b1bd16
+size 24231
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index aefafbc1e3..bfb9ec187a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cbd283f464eefde6a8f99d4546d89de62aa88aa57eea87115a11ac2de90c02a8
-size 55627
+oid sha256:04af2a4120f7bda8b883d68327f0a2f5d0ac4072a86d120354f2c3bfbde2ff27
+size 55652
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index a71e49a925..0e6eaae2f4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b334c7d8bb45aef8b7c6bb4f9b016f344283e36b45f762c49b0acac195b214eb
-size 30407
+oid sha256:f81e2754d4152b993f3572bfe0505ea5f88019e4133bc379c9e0eff28fcfac74
+size 30596
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index db80f0d5c2..f4667da358 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7198551e3d09343b6ba499349d7634b116d42707077ad31220493098051b3bff
-size 29152
+oid sha256:2aa961419706ae0409f7903f54634bea33342a35884f6ddf970279c4e0fdd512
+size 29325
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
index 99e13a9113..400b59412c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4493895c285381147d7ee542166fdc946b1fd1a8bcbdeb23df391f63e0f731fe
-size 29369
+oid sha256:5841e8de48a2ccac2f3cec3d228b2136fc8c876687e971869cdb4c8aa83b524b
+size 29413
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png
index f5566a7ddc..4bf226735e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d4131fae961f950e77a6c435703fd6193e9bcee80b5c0fea32bc9383c4db787b
-size 26352
+oid sha256:090bfe363dbe3ab64533549bd37db81b54962af57062e2ea09c45d5e5efec362
+size 26496
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 5a29b71fcb..5659fbd79d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f19d6be4e6e65007ee9548cb6494c6f760f8282f24ada04a53089a142fe8d0b7
-size 14028
+oid sha256:3c11260ddfc0a62f6d530096ab38f9d372d68383ba62f5edd081df776c300abc
+size 14454
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 195813c9b1..1e9422aff0 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:445afeb50307433ab21756b60d2d0f1f365db74a99d81db9ab994927d823ccaa
-size 28495
+oid sha256:79150e1801fc222685c31f2997476b68b41f3b0d99f16abf027aae373144d39a
+size 28585
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 7158d39702..dae86440d4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2947b905a604c522696036b5ab408ec22d2ec6dd8b75579c5727c6626776abdd
-size 11520
+oid sha256:db0f0208e41db2043aca84b138bdb114377a56764f8b548e35410d45c8d9fa2d
+size 11950
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 8124a1de37..05c06cc679 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7c7dcf21a708c134ac856b5b27c398330c73f25a69d9d46fc839e7aa8f2c02ef
-size 26291
+oid sha256:9473eed03afdfca91ba2f869767bdfa9cb426a2309517d9ed475cc765da005cc
+size 26623
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index e8ebb07f1c..2ae3ae37f7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6ea55df98f4ef544494dabbf661c1623d567291702a68993ab65f4a67770954f
-size 13576
+oid sha256:f7d7e82e1ca64dc7e18a6499e83d1deb1a1d7f55da70d0690f7c5f2bf2ae3675
+size 13976
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index 4c91cdb1a9..3959b492c7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cc5794eba22329603285fa264519fd9c4c7dae4ddb867ebe54f5c87f4af646fb
-size 45438
+oid sha256:d6a9233053173c4e85792c665df900ce4823508820e504cb76c2185f193a122e
+size 45527
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png
index 58e7a0d034..171d9ed71d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f67bdbeedafdaf59f1bb643d86dbfdf10442900d7d426e2e31cf62ed5fe43d9d
-size 38555
+oid sha256:64970dcd4086509e796cd67fe862053d4c5b1eb14234647ccba6abbb978b2aed
+size 38643
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 751bfeff5d..d6d69dfc15 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a9c4c8b226405f3c92536e329eea5f8ed73b66f9bf8b18c484472212551ab13c
-size 15034
+oid sha256:72f6737a9e2612f6b3ca309150d7cfdb57e0a28793a4d9bbbc2b3ae280fd8205
+size 15397
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png
index e722eddaf7..301449fde7 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:71612a9c2685e71e2075d8f592833448fae446edeee64f996626fb713142d02b
-size 29353
+oid sha256:0cbb9fa12c3856df22741ecc4bcf9bba25fe113b8ca9f4bb0a7bf3e21232a0d4
+size 29491
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 85ea57bf0f..7efb05c9f8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dee1f76c085ea1779e79f6e3f1f67d63c154989bb07efff4af8261073980ed84
-size 12354
+oid sha256:7efaf993aaf2c43ed4ce4bbace39c6e19d4551bac1488f0efad1e9251090c457
+size 12715
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 767762b05a..ff73b1eef3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0b1dd6dc2e1c333203063f86a1dcf442ba6aabaf1a4210a8d6429f402ccda5a1
-size 27229
+oid sha256:cc38ae9ae3ce7d632c6599d600e6095be2453804837d556653c8ef3fc5d407e3
+size 27470
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png
index 596de325e2..4a64d0aefa 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2a0d5a7fe904c98ece401a8e91685f9f15f02647eb269acbc20d93ead9d5665a
-size 14439
+oid sha256:492e4ba89d4033242f4b22da255a241f86330ee3eeb244ef5102c9d4319e80b6
+size 14793
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png
index 3e1241affa..676e76b3d5 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:af4c302e55c391b0aa69a418f80f8df514157a9c30d3f33b28cf6c322c458182
-size 46942
+oid sha256:7911d3f2ce4cf00f97de32e7ef3775abc354352555428dfbdb7761fa2a4d79fb
+size 47055
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png
index 37ba85ea50..4771d083c4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:aca6a614db1bcaec2a0b8ad4c3a64c761fda757e32865f535e75f470fda97774
-size 40683
+oid sha256:cf6c53f13773361c289f05141935c591a96d0f91dec16504a3ba74cac7436862
+size 40831
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png
index 7c5af2c6d3..e7a9157488 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0e61170e31a1fb6d697e08c73e13dec40e88759f880a5c1720788a3b231800d7
-size 19587
+oid sha256:b32936346c597afcdc11ed72650e8651f77a5e2e3db88e12ae95bf65961c415b
+size 19662
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png
index 0e2b1f9c70..47fea63910 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:37722b8d9c417f8b1a22083dcf9f1d48b166e39c64f6e11713b6f85b6182ce6f
-size 17510
+oid sha256:a9143534f5405494e1cc2c0335d0652460d4a29aafb9efd1e1f6940c5917f281
+size 17542
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png
index 1eea369e16..ba77e9e73e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d979a957761c00848ca0ef3b115b5787d5a26eee5e4d4db6ba5b837c1f05be77
-size 20035
+oid sha256:5834c50a2faef01976abcf85b9d1800f4dd94fb02684fa2951d5367c326831f8
+size 20114
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png
index 7c5af2c6d3..e7a9157488 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0e61170e31a1fb6d697e08c73e13dec40e88759f880a5c1720788a3b231800d7
-size 19587
+oid sha256:b32936346c597afcdc11ed72650e8651f77a5e2e3db88e12ae95bf65961c415b
+size 19662
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png
index 7c5af2c6d3..e7a9157488 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0e61170e31a1fb6d697e08c73e13dec40e88759f880a5c1720788a3b231800d7
-size 19587
+oid sha256:b32936346c597afcdc11ed72650e8651f77a5e2e3db88e12ae95bf65961c415b
+size 19662
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png
index 50217fd9f2..71459b1241 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c7ca068387cff8faf728a989488e7c4b5b07983c4b24162ff82a14fd90b82d05
-size 20609
+oid sha256:213318b5c5556a6667b9a48701f738e7c55a40c763f3bd79df05e321b1fdc74a
+size 20684
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png
index 10c5bccfea..ab29e8c5cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:57a16092b5e13be5f76f4cc42cf3e0c2bad983695a6b761fdb400a885c239ddb
-size 20078
+oid sha256:db61e15a906b09def466314727c21809c8019863ba4365e9bca54bca1f00bcfd
+size 20100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png
index 044efd7841..b02bda6284 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:802a6878cd7eaabdccea21f1d430f0e498ef7470ca963c6b2b869913d2a2d0a9
-size 17859
+oid sha256:755af35da6c6e3238201b016c8de7af8e5adba70904ab99f3320406490753b41
+size 17932
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png
index ef2f9cde70..6a4762181e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f1e93f5807f4a1dad7be55a918f2bcbdfb9ea1b95140bab26005812c3e363e1b
-size 20558
+oid sha256:bf0402f33d86d5b112c1ba6ae50f9135d87ba6fcad1f73add6eed56ae520b4ee
+size 20579
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png
index 10c5bccfea..ab29e8c5cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:57a16092b5e13be5f76f4cc42cf3e0c2bad983695a6b761fdb400a885c239ddb
-size 20078
+oid sha256:db61e15a906b09def466314727c21809c8019863ba4365e9bca54bca1f00bcfd
+size 20100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png
index 10c5bccfea..ab29e8c5cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:57a16092b5e13be5f76f4cc42cf3e0c2bad983695a6b761fdb400a885c239ddb
-size 20078
+oid sha256:db61e15a906b09def466314727c21809c8019863ba4365e9bca54bca1f00bcfd
+size 20100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png
index cab17c4d00..0306b6170d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e1599be0c5a37083c015378ee47a78a90dcf670f4df5d85dd25d39f4b2edbdf6
-size 21147
+oid sha256:edd5b6edd1e7b3217ae5502758de3c65333244c308ae1280a61d99e10c3438c5
+size 21167
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 8952186a4e..f916d90928 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8850f5c0e52e9441f92010bebe8828cdf5e3715250aadb68f31d996ec1fb7520
-size 38042
+oid sha256:b7a8ca9eb62e3988fd6adc3d3992bb6a8577b91b44d05a6c45a4af48b3bdbb4f
+size 38282
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 9db420c070..508f583f10 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:41c107c84e391b34ca13210b904327ca348625393339b95de8e740ff231c53e8
-size 13120
+oid sha256:d9bc5229a0ea1f347f5030b2bb41e656a999c001958e44ec1aaa1b70bd612017
+size 13104
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 864c9083a3..3c3271d44f 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8338dfb26935d63918749a3dd0c3d06d752172aba69bc67969b4928667fa8da1
-size 39183
+oid sha256:29c635e2bfb59cea20c8ee2c4c008a2edc74127313c437d63f7e4ef4f0851921
+size 39425
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 1fb5bc6b4a..f5dba656cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a1ac52a5f865e8adcb8c6429e0d6a7f088bad2f0607b3362f899f3b87526aec6
-size 14016
+oid sha256:93a44977870bc32a5f4c742b508ddb4c884e1a65701c085eece2e576c61f26a6
+size 14053
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png
index 41e47678d2..575ae56660 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:41972465c065b84fe047cf59394a7e83c036ef21b2eed5e2ec8c8e57f76a1408
-size 53943
+oid sha256:34d2fd15e0a0d432b9e9399e69ae14e899676a7534f5860242d7fa7ec091b1ef
+size 54000
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png
index e4cc063245..7929b4f76d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a744e1072897c73906d1e87931586c9124b73c066e0fa4ab7d2c5d41d4abb344
-size 45347
+oid sha256:ebe4c5e10556936d62f1545e84605d9daf9ca3de3cef3d3f0dbe9bb02f1cf1b1
+size 45415
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png
index 13164ca1d8..80960d8c10 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9b0d8fcf96f22d9f7c4d8b417c2dfa8956b46866c0123ad365122c3080d4ac25
-size 46231
+oid sha256:fb0b6c742691a788fb44896f738b936dba95c6c16eefbb9bd593aa6e6bfe4480
+size 46309
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png
index 190dfb7155..542f79349c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c134e38cf600120bf43d0329042c6b24ccd2fa25bd48c70c028606a8598dd546
-size 48409
+oid sha256:71a7a8ac71a073a6f4ce65b7ad3a4a2a35dd1512d7ecc4cdbf53c9d52be4dba1
+size 48463
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png
index 85a3e18440..361ab0f9dc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:855bd0ec680f2e8f53d7e186c2f6228839f39c6e7b56ce3361f6b71740584ccf
-size 60275
+oid sha256:e73465a898e07b1c0d78aa87a771b574d50f536655dd63209426b79e1156efcf
+size 60356
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png
index 85a3e18440..361ab0f9dc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:855bd0ec680f2e8f53d7e186c2f6228839f39c6e7b56ce3361f6b71740584ccf
-size 60275
+oid sha256:e73465a898e07b1c0d78aa87a771b574d50f536655dd63209426b79e1156efcf
+size 60356
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png
index ec3d5c8824..13872ec88b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:368f31f5485fe14f49b727c05328027ed7026bc6ec466f0c08d013832abfa084
-size 49190
+oid sha256:a8ea686919da2a33f9ecde5efb70ac9c434a24c28c30138f425acc56350a5590
+size 49227
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
index 8d253ef56a..02b9bfb526 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6e7796a991784b30ec4117d304d7eecc09777019703bba1eb97deacf175f566c
-size 54205
+oid sha256:8242b5826755b17c8a1bd919bc87358b701fc8431ba107c7624c69d2d6a61085
+size 54268
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png
index 10ddb50653..dec71d834c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f1a1489ff6a9e2929209aac79d57b4ec8868e7a17d4830bfac4cc764a1af4714
-size 56054
+oid sha256:34ca7db5dc561482c657b7617740848aafdbd144fe755276d8cf92f5dbccf077
+size 56119
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png
index 0955f6d69b..9765798cd8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7862b2b99c0879ec4fda3904795cecb87a7beb6dc317635c4efcaf815a7eb6b4
-size 47428
+oid sha256:bd706136ddd94395eb8587e068578d281f5a4d20908121e4584bf98459533299
+size 47485
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png
index 0ec49ca16f..7394a4c8bc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7e22a7534c46b166706ea0161f57b1b826d31e2a6ed971afcec7878049e820f8
-size 48394
+oid sha256:7007f5a568a31a9580ceea6c694dd3311066ef22c76eaf50e5e62476b48ae31f
+size 48465
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png
index 3e8ac11ef0..4a1333ff1a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:34862731fdb1ae399e38ba2f45fef6a976241652b1188ee66e3d8be4c5fb49d1
-size 49668
+oid sha256:c8d76d954e33c53fd9d658e8cef59ca7c2ffbd0f51322572a99e8e74803d9a9c
+size 49731
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png
index 1800fdf719..8b6ea82885 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:10b32d81962ba03e246ee359ce64fec133345459e5bafc9976f8c9e1f2409960
-size 62345
+oid sha256:613eb68e1d88b3374c5b23884f5c992c548e636bc958e215cf3e5fec34186a88
+size 62364
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png
index 1800fdf719..8b6ea82885 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:10b32d81962ba03e246ee359ce64fec133345459e5bafc9976f8c9e1f2409960
-size 62345
+oid sha256:613eb68e1d88b3374c5b23884f5c992c548e636bc958e215cf3e5fec34186a88
+size 62364
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png
index ad0685df73..f514a29a5b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:297ea8debad4e91466865615e197b51b4ef719fbba45d26993f4a7d82e6c9a85
-size 50504
+oid sha256:977d10c74342d1d25ce19add8793cca2b4d4b400881163e58c5f8298308e29e7
+size 50563
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
index 240dbb251e..57d3d81732 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0b062c4ff23b014d270fa414cef3870aa47eba6c5096075c1508000d16631d8e
-size 56292
+oid sha256:05588cb33208fb210e7953509edc739cf9401f713132a7b44a9883615a4423c0
+size 56365
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png
index 82120c314f..225ec899ce 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9a7fa18ff871febb39ed15e617a873cc77b7a2bb1f8263ca6078086d5cd9b7c9
-size 28752
+oid sha256:0c3327e517224d5896e737f357a0a6edfd54c034e6da3c6c3302d91a6122a336
+size 28604
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png
index 2182656f2c..8a2465e607 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3ed90625ca0a30b12cdf53c34afdc7a29f410be83588adf64da7290345810f15
-size 28893
+oid sha256:5a5545f6fc8a18336e655c50787b706cfb5fde10b6b075b16013c6c90d27726f
+size 28732
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 37560a5913..ae17140c2d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0334a218c09298714de3ad44fbc2dc310efe542cc21609902d962a5059194cec
-size 58860
+oid sha256:1a4308687706a3f04660151013d63dab0c674650b1c158a9fd41f35898ae0b80
+size 58675
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index 6ef66d5ddf..a613701e1a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8aa48154bf2cf3a6ec6da96e24a86c6a3f1e6eb11865c2301a4f2638e471bd34
-size 37408
+oid sha256:9c6477d7476baccee037e491930d99f7ea00e313d1285fdd882be3de99f705b0
+size 37376
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index f3c4d30238..dc017f51f6 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:05d71406e279c54b23d9a3731b624d643ab820c3cee180536d1d4cbb50fa0d3f
-size 62283
+oid sha256:8359eb7337d0b58151e282f7686ebfc75b9b046f6bacc9fa750cba6ff57866df
+size 62060
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 75268923f9..de27672e6f 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a8f155381ccc3f8833b58e076e797034dfa528bd55bafd982c34fb997e4db491
-size 39830
+oid sha256:5b589817850bc30e336e9d5e443b2153133991aa5ea919838bddef26d2b1df9e
+size 39840
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 936fe4c686..64f3005666 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9b17397fbe00c25f0ec2012c84daa0483d53a4618777444d4cfd180d50c3cfc1
-size 27247
+oid sha256:ec007743c395328cc4b276221aaec7bbfd71d04d79470f94cb6576785d04c6b3
+size 27088
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index 0c3abd6102..5d1f622540 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:52854ce12dcc8304004a2b6ebc2def5513dc390483f59d51ca30e149ec12d8e2
-size 56374
+oid sha256:085ec9629209f6b08ec436c95b7df7db4b0cc5bb87c3389aa4f23f8d02dc1158
+size 56221
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
index d9d8c00aa8..548fad8c57 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ac9abae6c6ac9b3b5d774f091ec89a40131b81a408624957cb16e33586f464e0
-size 57241
+oid sha256:7e3dc126bd7d1009b2b4296d22aa88047f7e71d1a5d9ff39523d2358cc4a34f2
+size 57563
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
index 0ebaa9612b..ac6fbdffcc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:115e7b766c41009e2ab75d0fc5b44e967e9e53e141bf36003fa8dfc1993cff83
-size 30679
+oid sha256:75248001be16310b2860afb8dd6eae5cbbb3b89d22467493a64e2dbb0ddb161f
+size 30563
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
index c432ea640a..4edb7f637a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:408ee6e94c45930a4c4ad7413e65eaf314a848f6bea7f3740a49cc68a7bd6f88
-size 25834
+oid sha256:0b149df2993eda450c861d97a78b36d823696b0068ab45552086eaaac92b49b6
+size 25699
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 3bf8f2f81e..3417705d4c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ee6e86ac798549910a5350dd371219ad0e0a1616d3f616e31dc2c38482ac2453
-size 28724
+oid sha256:3f13fc514ffc482bf2d863b6e8e2c305bef5568df9ceb21046714a9f6aed4592
+size 28594
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 36a54d5785..d4440d8324 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2b8870f4e25d40bac82e6f252f74d12f89a00b89b5ede2cdbfc613fbb3a4bc8f
-size 56838
+oid sha256:9497101bca7966cfa64cfc203201a20cc97add051dcd25b64d4297d0d4df8864
+size 56821
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
index 1c70233409..20937eae64 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:01485b9e3264e64273d720474b947b3372b7b3368b8eb5b575d4ca1593e0eb9d
-size 58058
+oid sha256:53dd35c89ef71da224c1dcd677de042fa356f82040bdab8d2653a656ecc5fced
+size 58248
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
index 06d46ba33b..214dd48f88 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fe8368c7bc5a16f178c114391aa7a2f7801aeb4b73f8df0f2ad9bda1e690059b
-size 32121
+oid sha256:b943326eb8a8dfad99c7e47108a8459246d3f22c0f7fce55c2c9a30c404ed39b
+size 32050
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
index df4474d272..ea653c834e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5f8fe0565a38d640227b5d4ffd552d7fba2c0fea7978a3d578c34b1dbc36ed9d
-size 26670
+oid sha256:802e5a13191b96fd002f88b489cf3a353eb99ee721749a979d5cc4f1627938fe
+size 26576
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png
index 45788b5180..7686fdfe0b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6fbd442e62fc2c8e02040d0ef42be06af5a1d554ee386e983c4bccae199004aa
-size 9456
+oid sha256:b4cece3ca4908302854c57084313d89320e4b5d645a3b43406b9876d70deb2ea
+size 14549
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png
index f02f83b162..e4cc246047 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7f9e941ab8eceefdd858c81667a5bafa33a4efc7d232ad0d501547ecda37758d
-size 9616
+oid sha256:9031a4cbdc155e05c4aa56e774fc01ed09f32cbae7ab443cdeb42a2f2d41a8a2
+size 14942
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png
index dfdb106006..26fd28c090 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f35e88e2b096b989eb669b8fee4196025177b29a9c9b729b843dcf7978df6add
-size 7420
+oid sha256:483bc7356a610804ec07c2794108850dafe04b4c86b653b0199fe0f5b8b85567
+size 7743
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png
index 0cff850500..8923f6297f 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:60c7dc93e9f13f1452473f783e7ecf7fc776610108a0a5e8fdf92c9e4f93a6a3
-size 7528
+oid sha256:5fd715882bbac80af31176491c97c2af711a4454a88832a086a55e5ece04e255
+size 7821
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..08f5a24c49
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ebb2d33b0f8aa473b9deb44e57df4edec1346f164098eec87163ebd5c8db9ebd
+size 5314
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..52309e0c28
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b71348d0a316569c389682f5904c2d5b67180bd6c4ae13112d81afdb6cdada6
+size 5273
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png
index c84d42e293..c6a4eab5da 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:12075b74ecb0d505b38c99bac410d4195ed85e6cf8efd2ac07848962b7970238
-size 10972
+oid sha256:7b354e5d775173757829e50113b4859afd0883f0441ebd22d05545e2e882a3dd
+size 11177
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png
index 926dab7b5b..ca80beb9e9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d7e2f73cd2a33a946b256dffb97ff4485a17040a2f7358a4773bfe028ccd9e4c
-size 11042
+oid sha256:7aacb9ba54c904060cc40ab5e5725d92a152c9f3b9441fa2fb6d0fdd8449583a
+size 11230
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png
index 3713f657e8..b7bbe45340 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0186b4f2d64220e1b0341f5007f0577d82a0c0424ad53937b6669e772fbecc4d
-size 7711
+oid sha256:c37c5cb1394da3a0b28fdd749f356f8a5d132f5408a88bc6e367052452a828d0
+size 7607
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 31e652eda3..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f8b30dd030cae4bd394796da45e3e067c7aca5fb5176bdd73a8a24938f8571a1
-size 17707
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..1fd95a27de
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c66c42d9c687bb41743117b83c65e348f5fb921865070afcf3352b618f79937
+size 23759
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png
deleted file mode 100644
index 178db99946..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a66a098b4d029369637ffaf9d5ac24f7a76d168b526382c78167132756476c2c
-size 22945
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png
deleted file mode 100644
index 7376ab8e10..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8a32ebcf17a8906570bc08df47cccac1ff4b5c7f7cd0bc414a188c9e302f6934
-size 22931
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png
index 4e240079ab..42ee6936cb 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5c0be8e52cbd3d139601bcfe093174fa5d873f2433709a1306645062c9a8bfb9
-size 17921
+oid sha256:0e42b60613f27563e3d6921854fb9feccdd17ba7ce332a40e954b4b3bafb969f
+size 17468
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png
index ecace25254..b57806c97e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:73c68fc2181960285410a51007a033b461abbd9ad89844d18eae10dcda26bdd4
-size 21302
+oid sha256:b939ee5229bd4c0f1d5533b492cbb24741a98e20cd8a848aca3ab02527cfc818
+size 23228
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png
index 9d7ab1352b..29f5373201 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:22747c979bf704a554dfa4ee3ace1551e78f1180bc594c55f1497ec1d6529aa2
-size 21211
+oid sha256:fcd06348851e32f3c08bc30e774006c493d9dc91402840af8edd057377a27acd
+size 21618
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png
index 767844a2e8..7afa167e0a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c9dadf14d9538191b4f9be41d8deb4e441008d80eb687d3f01484d6e497dcadb
-size 14291
+oid sha256:49a7579caea03ac759e5201bb88e131cad9447afbad3c250703ece49cf025e6b
+size 13561
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png
index cd36276b8a..050cbf8497 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:16f731c6c1d2cb9b462b22ab56475fda53844747eb0c587d801456903c3d7bcf
-size 13697
+oid sha256:df1ee64f9a226fc3113caedab7b7e7450d155477b52694f134cf4da53523cfb6
+size 13124
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..60a3b8f39e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:373fcfdea163cd59d1b929adb1abf7163c0e1aaa203033292931fd22cd5c60d6
+size 22378
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9a02d72493
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1e171c6295cd93af0fc0533b8c3099c75d5343132477709e231848c0c51b35f
+size 24366
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..9600daf12b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45485288d1cd0552c371aa868600e1fd8c30232ea2d9cb26420ec36c539cdb6f
+size 28228
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png
index 22efc21bb1..bb199bf0cd 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8c539176a004d4664f8f0eba24cdc00950b42284dcab249ef3bc87b0347df85d
-size 32949
+oid sha256:0192cbadf07b54899608c57e094fd30d86a9856a4354b9e540a1ce05d4da393d
+size 33460
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png
index c1cdbcc5b5..f26aa74393 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ee41ce428593fc4d8f329e623314c2200a55cb99950e6896f8df17bef5f7f3a1
-size 34187
+oid sha256:6fe1c01d5b8b16c1c6d6a8ff427a6050ce9347676ce87523fc93d5a69ae244bf
+size 34582
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png
index 5fbf7f31c1..3fc3b275fe 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:899b8d8bcb319ccf50867ce0e6f1333eddbcb232427098ea15fcacd37bdd6f43
-size 35981
+oid sha256:5ce469fe16d6b06c0425082ae37bde3c1931a69e390dd85a6f05ac7619a0e001
+size 36525
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png
index 51138d72e5..8e81c65ca3 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ab443d66a938f968fab3ce8c26d7b567dab72ecc3c505ab1fa9f87c30822057c
-size 25451
+oid sha256:ae68c7bef1afc22da25dd84ff9068119a1c8e77ea5a6688e930d8f633f9ca654
+size 25548
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png
index e17d053854..f3f354976e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7c4db502fdc68ba5c1d391bc7b14b9a5c852154f0c1ee48788ebfddaa8bac21e
-size 26441
+oid sha256:9107edd0eec824d18b2cc1d4a8c3263979a946fbc1f919772f7f6e65f7a91d0e
+size 26601
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png
index 8d195dfdef..4444536299 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6dc928db8fab90762a934c10dfc8d50aa0ed7ebe776bd2705c4f0cba2eac74fb
-size 11262
+oid sha256:7cf38f5423e4d7163cf29495b01b5dc716f348a50f9c9683d293d1189d10b71c
+size 11681
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 4cd2f5661a..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:265ddc2f480f343efbb8717a66f5f933714e5320e837f0d26d825ed595612f2d
-size 21039
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png
index 3c6579753b..fee0b4bc86 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c7f0987b11f1ecc5e8359b668ac8cf29f0d5b6ef6f5c74d3660e68696be699ae
-size 7219
+oid sha256:c591956dbbc4265381b6bff537efe21bdb70f3ab14862b63976ff2038b193dc6
+size 12176
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png
index a6ece5f74f..4d3cfde8d2 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5f470f47f972d0c948a308622e49855bb88365675d8b251cd1cce9972f5569c9
-size 6878
+oid sha256:b47ed55d8919fd15da1e3877df6fa32a58bd51ebd676a7ff303e09ddb1d2d7fd
+size 11604
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index 53e4ece077..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:3ffe5901b54ac442c8b71a7ff1d59bb3903ef2374c6fad378e55fa857e2ff34c
-size 23162
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..4bc307ba70
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fcae0955e194993a9cfe55a4279d7b9b601d4fa91e6ff67962d9339ec32eb952
+size 43832
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ce6e549f68
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:13c793c97dc51d2cef3e3590efb65957e58bf77501f5791b2a0a8edb01a48c5f
+size 42358
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png
index f6c9fc64dc..d25604d927 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:19db35d98bc8f6e4525e297616b564c9c2acbc2e9f7422292aca4a5d485c4314
-size 7682
+oid sha256:0de49c41130b525fd6baf687ccd63b3d98997d9a31d23a0ea826d0dbe7c1998e
+size 10404
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..757ac6640e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3093cd24a222f9854c3927fdd3fb39d26b1ad602b6e8580966ecae36ff937358
+size 47957
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2cdea2bdc1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8c6b89df7fc0d8cda26ec10ffcb5c5ca266e913c75da9f1943a22b6bb56691f9
+size 47167
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index f918f32903..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6efea5a0c9cd8e5626c16248e020b88a069ce878b4471e93dd9ccb956ddd41df
-size 26956
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b64970ee0e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b57120ea3e2b2ca243a738000f89aa3bbc6d9bc87d76d597b594165a5d4c489d
+size 32489
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..afa63e44bd
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2fb4d0d2ce2c59edf9f55c30e9221b192dd421cb83e56acb7c1ae7218ceff761
+size 30776
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png
deleted file mode 100644
index ebcd57bd3b..0000000000
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:640035df43196f84a7031578feb4b54f9702801c7974fc7af6a80eb907c40097
-size 17486
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f9ba6a7ce2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc702968f26dcbc042d1c44de84cf200cd1b08e6c160bf454ea8ec5ee002b63a
+size 50858
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..ab9564627c
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9bd1e84d97db0588e6bea339acab2aec499c99730e93381f8a019d6201e129f7
+size 56998
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..6665e0a034
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18d9b58bdba48472cc32110e4d032b7b4a4cbc80cdf9e8ee44964fdbdde5e976
+size 55508
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png
index 4c91805426..305257631f 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1765f4842cbd6e3c1b7daa9a9cc13fc0f4c47ff73c2cb1fdd7f3d2377eea0553
-size 9053
+oid sha256:e425d58f766655f1003c595637ab7387cdf74ba7ae7862bf2f137f3ffd33f3f3
+size 21202
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2f67b451b6
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b756303958d8234c0e8c0f9ca16335c54d8bd610c73790058d6389eb7c8c3fae
+size 4875
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..a686a3827e
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:313860648fc6bf47ec98182ff018c54df61703fd8c1e302811a1e3a79a35b72a
+size 15933
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..c112d357b8
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:92ffb912aeeea5d161bd8c94ce7ac865eee13de50a7bc15217bfd0955e5a9b32
+size 18516
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..fd4c9b3d18
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55e08f85f71dc07addee6079425158831ed50209f4a43fad9a8d0252d55d0f4a
+size 19486
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..84918c94b2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de21230c271bc78f767f130e07403d778ac65d347cbb4ba564d883c679a283ad
+size 20084
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..b2689c1029
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:62b5669a3f1a1138f5b982d27a1d4b2f0a2f79e862a4e50eb238c1d09de3d390
+size 18833
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png
index 84068bb6f7..e4e7faaccd 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:eb51ba92db07682afe5727078f8e8b0a44cad16566b97430ca92132689bf25e7
-size 10188
+oid sha256:30dc47ea866c8d8c1faab02ce12e9d00441a70b778bb267a3c52a5eb8635e6ad
+size 16686
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png
index b7dfb49de8..97f012d11b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ae7c673ab96eaa30a72ffa4b960b93f7c5971be1155aba7d03ac4c43652098ca
-size 16361
+oid sha256:88c7be1a8cff74b057ee0f7ba09922c814c8c0b6a3dbaa69ea58ec287c425fca
+size 14357
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..209eac0f23
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3812e3a220997be68aca7817c6c93987009f81079aa979161f297d529b91d8a1
+size 21505
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 7bdf26fe6c..80b9d8d46d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:254850b3a638c4d54d9f6642c15caf3e4037c47ce50ae48102e2346df6e8947f
-size 29282
+oid sha256:b49f3a925c6652957bbb90c71d2dc47618d47cf76ceec50d71d9ee8758a41ea8
+size 29262
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 8bdc8eb362..8ab9358d5b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b8f5077f75ee156faae31e73d464840fda51f82b0666f0a8e661295eb193b133
-size 27442
+oid sha256:3a12ecd968f65e56fb995dcc05f7c9e3160a37dc1d9a95c8b6c52ba488fb1b22
+size 27454
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png
index cb3c20fb27..1446cdefd5 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4f59b518dafea7d576a888661d740afeb1a6e72d432527a5f27d4a0a695eab35
-size 29718
+oid sha256:f428d22f64900a84904f02d7f49c0c7e9b295facaba3b86cec7175d98385dca9
+size 29663
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png
index 26e1ff331d..fb2e423ef8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7988848232d0f44f6fd04b1a8a8ce17681659be7e63a9f68f4f3743d51f3a679
-size 29382
+oid sha256:26a2304fe3e5ad79241843a8dfdb9cb81d47a8cd4b4c9ce7cc0552d8be91d03d
+size 29404
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png
index 1ab026adf0..6da1f135d4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3f6792f7963608fd167889be3e657ac6ca6eafa6949bfbf03fa21f9a07b34573
-size 115873
+oid sha256:d192c21d0da760d33517a226e93d8e994d6a83bbcebc422deae1dcca7e512f71
+size 115822
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
index 0ce9bb484a..22750d6377 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:583910d40d3832aec4f2f1970f203e208ff6c8d4ea4a77fbbb3012a16d74b1d7
-size 23588
+oid sha256:922897d98edecf37a08b818edf88ca5ab2a62b810d3b8d1d70fdff611ec30e4f
+size 24272
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
index 4f1e3774a2..1ba3aa364c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:eece69f07544c8e61080cdc4391a12bbb52b30759a330a0967678af73112fda5
-size 32910
+oid sha256:e1a418d5bad67aba70ffba07e6d7d44b19962ea8433f4aa050b58784874a83c4
+size 33567
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
index e1933a744c..dfbfd18cfa 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9a5a75e7614025e08e320771628ddb7e3b24f055bc5ced0785b9b259f183200c
-size 27249
+oid sha256:0920fdd5ec77504fb5b94e3e77dd1afade7f917282b2fecab2fc30d986a34ca1
+size 27885
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
index 38772ec2da..8f982d38ac 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e8cb222bf65bcc884814d8af92f032a79a2bdbf6f66f5ca792e7f72ad3917fbe
-size 24442
+oid sha256:9525fb2cb0576268b8af4eed06d882d623a8f78e8581f6c9a63bf74403c88909
+size 26018
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
index d0f492d482..06367fb9b9 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a277bf8619ef6f8d52a83d33e855f03e28dfea14e2af2482db3f7ce1a89c35c4
-size 34396
+oid sha256:bca874faf9c1d1525744d8a4942df1d4f019a71aa4faf6ec9a8d4d368483a1ef
+size 35855
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
index 94861e6fda..d860328261 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5d61c7c8f802a9f1bfefbf4d2b0e666a9e2162fa13bab523584baedf380c1df6
-size 28484
+oid sha256:e1b3a38d1d502cf9fb54517f82afeaa7c47a0bb9968273a1e0a76ea60e181798
+size 29711
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png
index 70da769e1f..a8f9374795 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:484c56c1ab68d394200202bda364175a1fbd27df31630649010852a4c2552870
-size 20801
+oid sha256:3cd64fd3c8647a179c8cccb46878a3495bf2a41173c62d0a378c8ff003dfe7bc
+size 20148
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png
index f2a152da09..a5b012cc36 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b8d6b5bcaa10f4ae2142edfef6745e22e0e9f8f52d9fedcbe77d3523ec4cb48d
-size 21476
+oid sha256:f458452cceef79d94ab3875c67e1664c60f13b77eb3ede7c0cdc04538bed9298
+size 21730
diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh
index 9e8c964499..f37d661379 100755
--- a/tools/check/check_code_quality.sh
+++ b/tools/check/check_code_quality.sh
@@ -38,7 +38,7 @@ else
fi
echo
-echo "Search for forbidden patterns in code..."
+echo "Search for forbidden patterns in Kotlin source files..."
# list all Kotlin folders of the project.
allKotlinDirs=`find . -type d |grep -v build |grep -v \.git |grep -v \.gradle |grep kotlin$`
@@ -47,9 +47,20 @@ ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt $all
resultForbiddenStringInCode=$?
-if [[ ${resultForbiddenStringInCode} -eq 0 ]]; then
- echo "MAIN OK"
+echo
+echo "Search for forbidden patterns in XML resource files..."
+
+# list all res folders of the project.
+allResDirs=`find . -type d |grep -v build |grep -v \.git |grep -v \.gradle |grep /res$`
+
+${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_xml.txt $allResDirs
+
+resultForbiddenStringInXml=$?
+
+if [[ ${resultForbiddenStringInCode} -eq 0 ]] \
+ && [[ ${resultForbiddenStringInXml} -eq 0 ]]; then
+ echo "OK"
else
- echo "❌ MAIN ERROR"
+ echo "❌ ERROR, please check the logs above."
exit 1
fi
diff --git a/tools/check/forbidden_strings_in_xml.txt b/tools/check/forbidden_strings_in_xml.txt
new file mode 100755
index 0000000000..ee0e4c7136
--- /dev/null
+++ b/tools/check/forbidden_strings_in_xml.txt
@@ -0,0 +1,38 @@
+#
+# Copyright 2023 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.
+#
+
+# This file list String which are not allowed in resource.
+# Use Perl regex to write forbidden strings
+# Note: line cannot start with a space. Use \s instead.
+# It is possible to specify an authorized number of occurrence with === suffix. Default is 0
+# Example:
+# AuthorizedStringThreeTimes===3
+
+# Extension:xml
+
+### Empty tag detected. Empty translation or plurals?
+">
+">""
+
+### Rubbish from merge. Please delete those lines (sometimes in comment)
+<<<<<<<
+>>>>>>>
+
+### "DO NOT COMMIT" has been committed
+DO NOT COMMIT
+
+### Tab char is forbidden. Use only spaces
+\t
diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml
index a3bad54ab3..14193a1c70 100644
--- a/tools/detekt/detekt.yml
+++ b/tools/detekt/detekt.yml
@@ -1,6 +1,21 @@
# Default rules: https://github.com/detekt/detekt/blob/main/detekt-core/src/main/resources/default-detekt-config.yml
style:
+ AlsoCouldBeApply:
+ active: true
+ CascadingCallWrapping:
+ active: true
+ includeElvis: true
+ DataClassShouldBeImmutable:
+ active: true
+ EqualsNullCall:
+ active: true
+ EqualsOnSignatureLine:
+ active: true
+ ExplicitCollectionElementAccessMethod:
+ active: true
+ ExplicitItLambdaParameter:
+ active: true
MaxLineLength:
# Default is 120
maxLineLength: 160
@@ -9,38 +24,117 @@ style:
ReturnCount:
active: false
UnnecessaryAbstractClass:
- active: false
+ active: true
FunctionOnlyReturningConstant:
active: false
UnusedPrivateMember:
- # TODO Enable it
- active: false
+ active: true
UnusedParameter:
- # TODO Enable it
- active: false
+ active: true
+ UnnecessaryInnerClass:
+ active: true
+ UnnecessaryLet:
+ active: true
+ UnnecessaryParentheses:
+ active: true
+ allowForUnclearPrecedence: false
+ UntilInsteadOfRangeTo:
+ active: true
+ UnusedImports:
+ active: true
UnusedPrivateProperty:
- # TODO Enable it
- active: false
+ active: true
ThrowsCount:
active: false
LoopWithTooManyJumpStatements:
- active: false
+ active: true
SerialVersionUIDInSerializableClass:
active: false
ProtectedMemberInFinalClass:
- active: false
+ active: true
UseCheckOrError:
+ active: true
+ OptionalUnit:
+ active: true
+ PreferToOverPairSyntax:
+ active: true
+ RedundantExplicitType:
+ active: true
+ TrailingWhitespace:
+ active: true
+ TrimMultilineRawString:
+ active: true
+ trimmingMethods:
+ - 'trimIndent'
+ - 'trimMargin'
+ UnderscoresInNumericLiterals:
+ active: true
+ acceptableLength: 4
+ allowNonStandardGrouping: false
+ UnnecessaryAnnotationUseSiteTarget:
+ active: true
+ UnnecessaryBackticks:
+ active: true
+ UnnecessaryBracesAroundTrailingLambda:
+ active: true
+ UseDataClass:
+ active: true
+ allowVars: false
+ UseEmptyCounterpart:
+ active: true
+ UseIfEmptyOrIfBlank:
+ active: true
+ UseLet:
+ active: true
+ UseSumOfInsteadOfFlatMapSize:
+ active: true
+
+coroutines:
+ GlobalCoroutineUsage:
+ # Keep false for now.
active: false
+ SuspendFunSwallowedCancellation:
+ active: true
+ SuspendFunWithCoroutineScopeReceiver:
+ active: true
empty-blocks:
EmptyFunctionBlock:
active: false
EmptySecondaryConstructor:
- active: false
+ active: true
potential-bugs:
ImplicitDefaultLocale:
- active: false
+ active: true
+ CastNullableToNonNullableType:
+ active: true
+ CastToNullableType:
+ active: true
+ Deprecation:
+ active: true
+ DontDowncastCollectionTypes:
+ active: true
+ ElseCaseInsteadOfExhaustiveWhen:
+ active: true
+ ExitOutsideMain:
+ active: true
+ ImplicitUnitReturnType:
+ active: true
+ allowExplicitReturnType: false
+ MissingPackageDeclaration:
+ active: true
+ excludes: ['**/*.kts']
+ NullCheckOnMutableProperty:
+ active: true
+ NullableToStringCall:
+ active: true
+ PropertyUsedBeforeDeclaration:
+ active: true
+ UnconditionalJumpStatementInLoop:
+ active: true
+ UnnecessaryNotNullCheck:
+ active: true
exceptions:
TooGenericExceptionCaught:
@@ -48,11 +142,13 @@ exceptions:
SwallowedException:
active: false
ThrowingExceptionsWithoutMessageOrCause:
- active: false
+ active: true
TooGenericExceptionThrown:
- active: false
+ active: true
InstanceOfCheckForException:
- active: false
+ active: true
+ ObjectExtendsThrowable:
+ active: true
complexity:
TooManyFunctions:
@@ -66,24 +162,32 @@ complexity:
NestedBlockDepth:
active: false
ComplexCondition:
- active: false
+ active: true
LargeClass:
- active: false
+ active: true
naming:
VariableNaming:
- # TODO Enable it
- active: false
+ active: true
TopLevelPropertyNaming:
- # TODO Enable it
- active: false
+ active: true
FunctionNaming:
active: true
ignoreAnnotated: ['Composable']
+ LambdaParameterNaming:
+ active: true
+ NonBooleanPropertyPrefixedWithIs:
+ active: true
+ VariableMaxLength:
+ active: true
performance:
SpreadOperator:
active: false
+ CouldBeSequence:
+ active: true
+ UnnecessaryPartOfBinaryExpression:
+ active: true
# Note: all rules for `comments` are disabled by default, but I put them here to be aware of their existence
comments:
@@ -143,15 +247,10 @@ Compose:
PreviewNaming:
active: true
PreviewPublic:
- active: false
+ active: true
# You can optionally disable that only previews with @PreviewParameter are flagged
previewPublicOnlyIfParams: false
RememberMissing:
active: true
UnstableCollections:
active: true
- ViewModelForwarding:
- ## TODO Set to true later
- active: false
- ViewModelInjection:
- active: true
diff --git a/tools/gitflow/gitflow-init.sh b/tools/gitflow/gitflow-init.sh
new file mode 100755
index 0000000000..5068a37575
--- /dev/null
+++ b/tools/gitflow/gitflow-init.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+#
+# Copyright (c) 2023 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.
+
+git flow init -d
+git config gitflow.prefix.versiontag v
diff --git a/tools/github/download_github_artifacts.py b/tools/github/download_github_artifacts.py
new file mode 100755
index 0000000000..892a4affa6
--- /dev/null
+++ b/tools/github/download_github_artifacts.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 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 argparse
+import hashlib
+import json
+import os
+# Run `pip3 install requests` if not installed yet
+import requests
+
+# This script downloads artifacts from GitHub.
+# Ref: https://docs.github.com/en/rest/actions/artifacts#get-an-artifact
+
+error = False
+
+### Arguments
+
+parser = argparse.ArgumentParser(description='Download artifacts from GitHub.')
+parser.add_argument('-t',
+ '--token',
+ required=True,
+ help='The GitHub token with read access.')
+parser.add_argument('-a',
+ '--artifactUrl',
+ required=True,
+ help='the artifact_url from GitHub.')
+parser.add_argument('-f',
+ '--filename',
+ help='the filename, if not provided, will use the artifact name.')
+parser.add_argument('-i',
+ '--ignoreErrors',
+ help='Ignore errors that can be ignored. Build state and number of artifacts.',
+ action="store_true")
+parser.add_argument('-d',
+ '--directory',
+ default="",
+ help='the target directory, where files will be downloaded. If not provided the build number will be used to create a directory.')
+parser.add_argument('-v',
+ '--verbose',
+ help="increase output verbosity.",
+ action="store_true")
+parser.add_argument('-s',
+ '--simulate',
+ help="simulate action, do not create folder or download any file.",
+ action="store_true")
+
+args = parser.parse_args()
+
+if args.verbose:
+ print("Argument:")
+ print(args)
+
+# Split the artifact URL to get information
+# Ex: https://github.com/vector-im/element-android/suites/9293388174/artifacts/435942121
+artifactUrl = args.artifactUrl
+if not artifactUrl.startswith('https://github.com/'):
+ print("❌ Invalid parameter --artifactUrl %s. Must start with 'https://github.com/'" % artifactUrl)
+ exit(1)
+if "/artifacts/" not in artifactUrl:
+ print("❌ Invalid parameter --artifactUrl %s. Must contain '/artifacts/'" % artifactUrl)
+ exit(1)
+artifactItems = artifactUrl.split("/")
+if len(artifactItems) != 9:
+ print("❌ Invalid parameter --artifactUrl %s. Please check the format." % (artifactUrl))
+ exit(1)
+
+gitHubRepoOwner = artifactItems[3]
+gitHubRepo = artifactItems[4]
+artifactId = artifactItems[8]
+
+if args.verbose:
+ print("gitHubRepoOwner: %s, gitHubRepo: %s, artifactId: %s" % (gitHubRepoOwner, gitHubRepo, artifactId))
+
+headers = {
+ 'Authorization': "Bearer %s" % args.token,
+ 'Accept': 'application/vnd.github+json'
+}
+base_url = "https://api.github.com/repos/%s/%s/actions/artifacts/%s" % (gitHubRepoOwner, gitHubRepo, artifactId)
+
+### Fetch build state
+
+print("Getting artifacts data of project '%s/%s' artifactId '%s'..." % (gitHubRepoOwner, gitHubRepo, artifactId))
+
+if args.verbose:
+ print("Url: %s" % base_url)
+
+r = requests.get(base_url, headers=headers)
+data = json.loads(r.content.decode())
+
+if args.verbose:
+ print("Json data:")
+ print(data)
+
+if args.verbose:
+ print("Create subfolder %s to download artifacts..." % artifactId)
+
+if args.directory == "":
+ targetDir = artifactId
+else:
+ targetDir = args.directory
+
+if not args.simulate:
+ os.makedirs(targetDir, exist_ok=True)
+
+url = data.get("archive_download_url")
+if args.filename is not None:
+ filename = args.filename
+else:
+ filename = data.get("name") + ".zip"
+
+## Print some info about the artifact origin
+commitLink = "https://github.com/%s/%s/commit/%s" % (gitHubRepoOwner, gitHubRepo, data.get("workflow_run").get("head_sha"))
+print("Preparing to download artifact `%s`, built from branch: `%s` (commit %s)" % (data.get("name"), data.get("workflow_run").get("head_branch"), commitLink))
+
+if args.verbose:
+ print()
+ print("Artifact url: %s" % url)
+
+target = targetDir + "/" + filename
+sizeInBytes = data.get("size_in_bytes")
+print("Downloading %s to '%s' (file size is %s bytes, this may take a while)..." % (filename, targetDir, sizeInBytes))
+if not args.simulate:
+ # open file to write in binary mode
+ with open(target, "wb") as file:
+ # get request
+ response = requests.get(url, headers=headers)
+ # write to file
+ file.write(response.content)
+ print("Verifying file size...")
+ # get the file size
+ size = os.path.getsize(target)
+ if sizeInBytes != size:
+ # error = True
+ print("Warning, file size mismatch: expecting %s and get %s. This is just a warning for now..." % (sizeInBytes, size))
+
+if error:
+ print("❌ Error(s) occurred, please check the log")
+ exit(1)
+else:
+ print("Done!")
diff --git a/tools/lint/lint.xml b/tools/lint/lint.xml
index 914c9e7b68..db1a20701c 100644
--- a/tools/lint/lint.xml
+++ b/tools/lint/lint.xml
@@ -25,7 +25,7 @@
-
+
@@ -43,24 +43,11 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -81,7 +68,6 @@
-
diff --git a/tools/localazy/README.md b/tools/localazy/README.md
index c1ad1d0ee0..b0b5c7e980 100644
--- a/tools/localazy/README.md
+++ b/tools/localazy/README.md
@@ -16,6 +16,8 @@ Localazy is used to host the source strings and their translations.
## Localazy project
+[](https://localazy.com/p/element)
+
To add new strings, or to translate existing strings, go the the Localazy project: [https://localazy.com/p/element](https://localazy.com/p/element). Please follow the key naming rules (see below).
Never edit manually the files `localazy.xml` or `translations.xml`!.
diff --git a/tools/quality/check.sh b/tools/quality/check.sh
new file mode 100755
index 0000000000..c3f87b3018
--- /dev/null
+++ b/tools/quality/check.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+#
+# Copyright 2023 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.
+#
+
+# List of tasks to run before creating a PR, to limit the risk of getting rejected by the CI.
+# Can be used as a git hook if you want.
+
+# exit when any command fails
+set -e
+
+# First run the quickest script
+./tools/check/check_code_quality.sh
+
+# Build, test and check the project, with warning as errors
+# It also check that the minimal app is compiling.
+./gradlew check -PallWarningsAsErrors=true
diff --git a/tools/release/release.sh b/tools/release/release.sh
new file mode 100755
index 0000000000..20005d2816
--- /dev/null
+++ b/tools/release/release.sh
@@ -0,0 +1,320 @@
+#!/usr/bin/env bash
+
+#
+# Copyright (c) 2023 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.
+#
+
+# do not exit when any command fails (issue with git flow)
+set +e
+
+printf "\n================================================================================\n"
+printf "| Welcome to the release script! |\n"
+printf "================================================================================\n"
+
+printf "Checking environment...\n"
+envError=0
+
+# Check that bundletool is installed
+if ! command -v bundletool &> /dev/null
+then
+ printf "Fatal: bundletool is not installed. You can install it running \`brew install bundletool\`\n"
+ envError=1
+fi
+
+# Path of the key store (it's a file)
+keyStorePath="${ELEMENT_X_KEYSTORE_PATH}"
+if [[ -z "${keyStorePath}" ]]; then
+ printf "Fatal: ELEMENT_X_KEYSTORE_PATH is not defined in the environment.\n"
+ envError=1
+fi
+# Keystore password
+keyStorePassword="${ELEMENT_X_KEYSTORE_PASSWORD}"
+if [[ -z "${keyStorePassword}" ]]; then
+ printf "Fatal: ELEMENT_X_KEYSTORE_PASSWORD is not defined in the environment.\n"
+ envError=1
+fi
+# Key password
+keyPassword="${ELEMENT_X_KEY_PASSWORD}"
+if [[ -z "${keyPassword}" ]]; then
+ printf "Fatal: ELEMENT_X_KEY_PASSWORD is not defined in the environment.\n"
+ envError=1
+fi
+# GitHub token
+gitHubToken="${ELEMENT_GITHUB_TOKEN}"
+if [[ -z "${gitHubToken}" ]]; then
+ printf "Fatal: ELEMENT_GITHUB_TOKEN is not defined in the environment.\n"
+ envError=1
+fi
+# Android home
+androidHome="${ANDROID_HOME}"
+if [[ -z "${androidHome}" ]]; then
+ printf "Fatal: ANDROID_HOME is not defined in the environment.\n"
+ envError=1
+fi
+# @elementbot:matrix.org matrix token / Not mandatory
+elementBotToken="${ELEMENT_BOT_MATRIX_TOKEN}"
+if [[ -z "${elementBotToken}" ]]; then
+ printf "Warning: ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment.\n"
+fi
+
+if [ ${envError} == 1 ]; then
+ exit 1
+fi
+
+minSdkVersion=23
+buildToolsVersion="32.0.0"
+buildToolsPath="${androidHome}/build-tools/${buildToolsVersion}"
+
+if [[ ! -d ${buildToolsPath} ]]; then
+ printf "Fatal: ${buildToolsPath} folder not found, ensure that you have installed the SDK version ${buildToolsVersion}.\n"
+ exit 1
+fi
+
+# Check if git flow is enabled
+gitFlowDevelop=`git config gitflow.branch.develop`
+if [[ ${gitFlowDevelop} != "" ]]
+then
+ printf "Git flow is initialized\n"
+else
+ printf "Git flow is not initialized. Initializing...\n"
+ ./tools/gitflow/gitflow-init.sh
+fi
+
+printf "OK\n"
+
+printf "\n================================================================================\n"
+printf "Ensuring main and develop branches are up to date...\n"
+
+git checkout main
+git pull
+git checkout develop
+git pull
+
+printf "\n================================================================================\n"
+# Guessing version to propose a default version
+versionsFile="./plugins/src/main/kotlin/Versions.kt"
+versionMajorCandidate=`grep "val versionMajor" ${versionsFile} | cut -d " " -f6`
+versionMinorCandidate=`grep "val versionMinor" ${versionsFile} | cut -d " " -f6`
+versionPatchCandidate=`grep "val versionPatch" ${versionsFile} | cut -d " " -f6`
+versionCandidate="${versionMajorCandidate}.${versionMinorCandidate}.${versionPatchCandidate}"
+
+read -p "Please enter the release version (example: ${versionCandidate}). Just press enter if ${versionCandidate} is correct. " version
+version=${version:-${versionCandidate}}
+
+# extract major, minor and patch for future use
+versionMajor=`echo ${version} | cut -d "." -f1`
+versionMinor=`echo ${version} | cut -d "." -f2`
+versionPatch=`echo ${version} | cut -d "." -f3`
+nextPatchVersion=$((versionPatch + 2))
+
+printf "\n================================================================================\n"
+printf "Starting the release ${version}\n"
+git flow release start ${version}
+
+# Note: in case the release is already started and the script is started again, checkout the release branch again.
+ret=$?
+if [[ $ret -ne 0 ]]; then
+ printf "Mmh, it seems that the release is already started. Checking out the release branch...\n"
+ git checkout "release/${version}"
+fi
+
+# Ensure version is OK
+versionsFileBak="${versionsFile}.bak"
+cp ${versionsFile} ${versionsFileBak}
+sed "s/private const val versionMajor = .*/private const val versionMajor = ${versionMajor}/" ${versionsFileBak} > ${versionsFile}
+sed "s/private const val versionMinor = .*/private const val versionMinor = ${versionMinor}/" ${versionsFile} > ${versionsFileBak}
+sed "s/private const val versionPatch = .*/private const val versionPatch = ${versionPatch}/" ${versionsFileBak} > ${versionsFile}
+rm ${versionsFileBak}
+
+# This commit may have no effect because generally we do not change the version during the release.
+git commit -a -m "Setting version for the release ${version}"
+
+printf "\n================================================================================\n"
+printf "Building the bundle locally first...\n"
+./gradlew clean bundleRelease
+
+printf "\n================================================================================\n"
+printf "Running towncrier...\n"
+yes | towncrier build --version "v${version}"
+
+printf "\n================================================================================\n"
+read -p "Check the file CHANGES.md consistency. It's possible to reorder items (most important changes first) or change their section if relevant. Also an opportunity to fix some typo, or rewrite things. Do not commit your change. Press enter when it's done."
+
+# Get the changes to use it to create the GitHub release
+changelogUrlEncoded=`git diff CHANGES.md | grep ^+ | tail -n +2 | cut -c2- | jq -sRr @uri | sed s/\(/%28/g | sed s/\)/%29/g`
+
+printf "\n================================================================================\n"
+printf "Committing...\n"
+git commit -a -m "Changelog for version ${version}"
+
+printf "\n================================================================================\n"
+printf "Creating fastlane file...\n"
+printf -v versionMajor2Digits "%02d" ${versionMajor}
+printf -v versionMinor2Digits "%02d" ${versionMinor}
+printf -v versionPatch2Digits "%02d" ${versionPatch}
+fastlaneFile="4${versionMajor2Digits}${versionMinor2Digits}${versionPatch2Digits}0.txt"
+fastlanePathFile="./fastlane/metadata/android/en-US/changelogs/${fastlaneFile}"
+printf "Main changes in this version: TODO.\nFull changelog: https://github.com/vector-im/element-x-android/releases" > ${fastlanePathFile}
+
+read -p "I have created the file ${fastlanePathFile}, please edit it and press enter when it's done."
+git add ${fastlanePathFile}
+git commit -a -m "Adding fastlane file for version ${version}"
+
+printf "\n================================================================================\n"
+printf "OK, finishing the release...\n"
+git flow release finish "${version}"
+
+printf "\n================================================================================\n"
+read -p "Done, push the branch 'main' and the new tag (yes/no) default to yes? " doPush
+doPush=${doPush:-yes}
+
+if [ ${doPush} == "yes" ]; then
+ printf "Pushing branch 'main' and tag 'v${version}'...\n"
+ git push origin main
+ git push origin "v${version}"
+else
+ printf "Not pushing, do not forget to push manually!\n"
+fi
+
+printf "\n================================================================================\n"
+printf "Checking out develop...\n"
+git checkout develop
+
+# Set next version
+printf "\n================================================================================\n"
+printf "Setting next version on file '${versionsFile}'...\n"
+cp ${versionsFile} ${versionsFileBak}
+sed "s/private const val versionPatch = .*/private const val versionPatch = ${nextPatchVersion}/" ${versionsFileBak} > ${versionsFile}
+rm ${versionsFileBak}
+
+printf "\n================================================================================\n"
+read -p "I have updated the versions to prepare the next release, please check that the change are correct and press enter so I can commit."
+
+printf "Committing...\n"
+git commit -a -m 'version++'
+
+printf "\n================================================================================\n"
+read -p "Done, push the branch 'develop' (yes/no) default to yes? (A rebase may be necessary in case develop got new commits) " doPush
+doPush=${doPush:-yes}
+
+if [ ${doPush} == "yes" ]; then
+ printf "Pushing branch 'develop'...\n"
+ git push origin develop
+else
+ printf "Not pushing, do not forget to push manually!\n"
+fi
+
+printf "\n================================================================================\n"
+printf "Wait for the GitHub action https://github.com/vector-im/element-x-android/actions/workflows/release.yml?query=branch%%3Amain to build the 'main' branch.\n"
+read -p "After GHA is finished, please enter the artifact URL (for 'elementx-app-bundle-unsigned'): " artifactUrl
+
+printf "\n================================================================================\n"
+printf "Downloading the artifact...\n"
+
+ # Download files
+targetPath="./tmp/Element/${version}"
+
+python3 ./tools/github/download_github_artifacts.py \
+ --token ${gitHubToken} \
+ --artifactUrl ${artifactUrl} \
+ --directory ${targetPath} \
+ --ignoreErrors
+
+printf "\n================================================================================\n"
+printf "Unzipping the artifact...\n"
+
+unzip ${targetPath}/elementx-app-bundle-unsigned.zip -d ${targetPath}
+
+unsignedBundlePath="${targetPath}/app-release.aab"
+signedBundlePath="${targetPath}/app-release-signed.aab"
+
+printf "\n================================================================================\n"
+printf "Signing file ${unsignedBundlePath} with build-tools version ${buildToolsVersion} for min SDK version ${minSdkVersion}...\n"
+
+cp ${unsignedBundlePath} ${signedBundlePath}
+
+${buildToolsPath}/apksigner sign \
+ -v \
+ --ks ${keyStorePath} \
+ --ks-pass pass:${keyStorePassword} \
+ --ks-key-alias elementx \
+ --key-pass pass:${keyPassword} \
+ --min-sdk-version ${minSdkVersion} \
+ ${signedBundlePath}
+
+printf "\n================================================================================\n"
+printf "Please check the information below:\n"
+
+printf "Version code: "
+bundletool dump manifest --bundle=${signedBundlePath} --xpath=/manifest/@android:versionCode
+printf "Version name: "
+bundletool dump manifest --bundle=${signedBundlePath} --xpath=/manifest/@android:versionName
+
+printf "\n"
+read -p "Does it look correct? Press enter when it's done."
+
+printf "\n================================================================================\n"
+printf "The file ${signedBundlePath} has been signed and can be uploaded to the PlayStore!\n"
+
+printf "\n================================================================================\n"
+read -p "Do you want to install the application to your device? Make sure there is a connected device first. (yes/no) default to yes " doDeploy
+doDeploy=${doDeploy:-yes}
+
+if [ ${doDeploy} == "yes" ]; then
+ printf "Building apks...\n"
+ bundletool build-apks --bundle=${signedBundlePath} --output=${targetPath}/elementx.apks \
+ --ks=./app/signature/debug.keystore --ks-pass=pass:android --ks-key-alias=androiddebugkey --key-pass=pass:android \
+ --overwrite
+ printf "Installing apk for your device...\n"
+ bundletool install-apks --apks=${targetPath}/elementx.apks
+ read -p "Please run the application on your phone to check that the upgrade went well (no init sync, etc.). Press enter when it's done."
+else
+ printf "Apk will not be deployed!\n"
+fi
+
+printf "\n================================================================================\n"
+githubCreateReleaseLink="https://github.com/vector-im/element-x-android/releases/new?tag=v${version}&title=Element%20X%20Android%20v${version}&body=${changelogUrlEncoded}"
+printf "Creating the release on gitHub.\n"
+printf -- "Open this link: %s\n" ${githubCreateReleaseLink}
+printf "Then\n"
+printf " - copy paste the section of the file CHANGES.md for this release (if not there yet)\n"
+printf " - click on the 'Generate releases notes' button\n"
+printf " - Add the file ${signedBundlePath} to the GitHub release.\n"
+read -p ". Press enter when it's done. "
+
+printf "\n================================================================================\n"
+printf "Message for the Android internal room:\n\n"
+message="@room Element X Android ${version} is ready to be tested. You can get it from https://github.com/vector-im/element-x-android/releases/tag/v${version}. Please report any feedback here. Thanks!"
+printf "${message}\n\n"
+
+if [[ -z "${elementBotToken}" ]]; then
+ read -p "ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment. Cannot send the message for you. Please send it manually, and press enter when it's done "
+else
+ read -p "Send this message to the room (yes/no) default to yes? " doSend
+ doSend=${doSend:-yes}
+ if [ ${doSend} == "yes" ]; then
+ printf "Sending message...\n"
+ transactionId=`openssl rand -hex 16`
+ # Element Android internal
+ matrixRoomId="!LiSLXinTDCsepePiYW:matrix.org"
+ curl -X PUT --data $"{\"msgtype\":\"m.text\",\"body\":\"${message}\"}" -H "Authorization: Bearer ${elementBotToken}" https://matrix-client.matrix.org/_matrix/client/r0/rooms/${matrixRoomId}/send/m.room.message/\$local.${transactionId}
+ else
+ printf "Message not sent, please send it manually!\n"
+ fi
+fi
+
+printf "\n================================================================================\n"
+printf "Congratulation! Kudos for using this script! Have a nice day!\n"
+printf "================================================================================\n"