Merge branch 'develop' into feature/jme/add-simplified-sliding-sync-toggle
This commit is contained in:
commit
b974032e64
163 changed files with 4353 additions and 1579 deletions
2
.github/workflows/build_enterprise.yml
vendored
2
.github/workflows/build_enterprise.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
name: Build Enterprise APKs
|
name: Build Enterprise APKs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
variant: [debug, release, nightly]
|
variant: [debug, release, nightly]
|
||||||
|
|
|
||||||
4
.github/workflows/danger.yml
vendored
4
.github/workflows/danger.yml
vendored
|
|
@ -6,6 +6,8 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Danger main check
|
name: Danger main check
|
||||||
|
# Skip in forks, it doesn't work even with the fallback token
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
|
|
@ -13,7 +15,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- run: |
|
- run: |
|
||||||
npm install --save-dev @babel/plugin-transform-flow-strip-types
|
npm install --save-dev @babel/plugin-transform-flow-strip-types
|
||||||
|
|
|
||||||
2
.github/workflows/generate_github_pages.yml
vendored
2
.github/workflows/generate_github_pages.yml
vendored
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
generate-github-pages:
|
generate-github-pages:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
steps:
|
steps:
|
||||||
- name: ⏬ Checkout with LFS
|
- name: ⏬ Checkout with LFS
|
||||||
uses: nschloe/action-cached-lfs-checkout@v1.2.2
|
uses: nschloe/action-cached-lfs-checkout@v1.2.2
|
||||||
|
|
|
||||||
2
.github/workflows/gradle-wrapper-update.yml
vendored
2
.github/workflows/gradle-wrapper-update.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
||||||
- name: Update Gradle Wrapper
|
- name: Update Gradle Wrapper
|
||||||
uses: gradle-update/update-gradle-wrapper-action@v1
|
uses: gradle-update/update-gradle-wrapper-action@v1
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
target-branch: develop
|
target-branch: develop
|
||||||
|
|
|
||||||
19
.github/workflows/quality.yml
vendored
19
.github/workflows/quality.yml
vendored
|
|
@ -20,10 +20,11 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: Run code quality check suite
|
- name: Run code quality check suite
|
||||||
run: ./tools/check/check_code_quality.sh
|
run: ./tools/check/check_code_quality.sh
|
||||||
|
|
@ -77,10 +78,11 @@ jobs:
|
||||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: Use JDK 17
|
- name: Use JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -116,10 +118,11 @@ jobs:
|
||||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: Use JDK 17
|
- name: Use JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -159,10 +162,11 @@ jobs:
|
||||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: Use JDK 17
|
- name: Use JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -198,10 +202,11 @@ jobs:
|
||||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: Use JDK 17
|
- name: Use JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -237,10 +242,11 @@ jobs:
|
||||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: Use JDK 17
|
- name: Use JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -271,6 +277,7 @@ jobs:
|
||||||
name: Project Check Suite
|
name: Project Check Suite
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [konsist, lint, ktlint, detekt]
|
needs: [konsist, lint, ktlint, detekt]
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
|
@ -42,6 +42,7 @@ jobs:
|
||||||
enterprise:
|
enterprise:
|
||||||
name: Create App Bundle Enterprise
|
name: Create App Bundle Enterprise
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
|
group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
@ -49,6 +50,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
|
|
|
||||||
2
.github/workflows/sync-localazy.yml
vendored
2
.github/workflows/sync-localazy.yml
vendored
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
sync-localazy:
|
sync-localazy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Use JDK 17
|
- name: Use JDK 17
|
||||||
|
|
|
||||||
2
.github/workflows/sync-sas-strings.yml
vendored
2
.github/workflows/sync-sas-strings.yml
vendored
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
sync-sas-strings:
|
sync-sas-strings:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
# No concurrency required, runs every time on a schedule.
|
# No concurrency required, runs every time on a schedule.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
|
||||||
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
|
|
@ -40,10 +40,11 @@ jobs:
|
||||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||||
- name: Add SSH private keys for submodule repositories
|
- name: Add SSH private keys for submodule repositories
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||||
- name: Clone submodules
|
- name: Clone submodules
|
||||||
if: github.repository == 'element-hq/element-x-android'
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: ☕️ Use JDK 17
|
- name: ☕️ Use JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
|
||||||
77
CHANGES.md
77
CHANGES.md
|
|
@ -1,3 +1,80 @@
|
||||||
|
Changes in Element X v0.5.0 (2024-07-24)
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
### 🙌 Improvements
|
||||||
|
* Add icon for "Mark as read" and "Mark as unread" actions. by @bmarty in https://github.com/element-hq/element-x-android/pull/3144
|
||||||
|
* Add support for Picture In Picture for Element Call by @bmarty in https://github.com/element-hq/element-x-android/pull/3159
|
||||||
|
* Set pin grace period to 2 minutes by @bmarty in https://github.com/element-hq/element-x-android/pull/3172
|
||||||
|
* Unify the way we decide whether a room is a DM or a group room by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3100
|
||||||
|
* Subscribe to `RoomListItems` in the visible range by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3169
|
||||||
|
* Improve pip and add feature flag. by @bmarty in https://github.com/element-hq/element-x-android/pull/3199
|
||||||
|
* Open Source licenses: add color for links. by @bmarty in https://github.com/element-hq/element-x-android/pull/3215
|
||||||
|
* Cancel ringing call notification on call cancellation by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3047
|
||||||
|
|
||||||
|
### 🐛 Bugfixes
|
||||||
|
* Fix `MainActionButton` layout for long texts by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3158
|
||||||
|
* Always follow the desired theme for Pin, Incoming Call and Element Call screens by @bmarty in https://github.com/element-hq/element-x-android/pull/3165
|
||||||
|
* Fix empty screen issue after clearing the cache by @bmarty in https://github.com/element-hq/element-x-android/pull/3163
|
||||||
|
* Restore intentional mentions in the markdown/plain text editor by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3193
|
||||||
|
* Fix crash in the room list after a forced log out in background by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3180
|
||||||
|
* Clear existing notification when a room is marked as read by @bmarty in https://github.com/element-hq/element-x-android/pull/3203
|
||||||
|
* Fix crash when Pin code screen is displayed by @bmarty in https://github.com/element-hq/element-x-android/pull/3205
|
||||||
|
* Fix pillification not working for non formatted message bodies by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3201
|
||||||
|
* Update grammar on Matrix Ids to be more spec compliant and render error instead of infinite loading in room member list screen by @bmarty in https://github.com/element-hq/element-x-android/pull/3206
|
||||||
|
* Reduce the risk of text truncation in buttons. by @bmarty in https://github.com/element-hq/element-x-android/pull/3209
|
||||||
|
* Ensure that the manual dark theme is rendering correctly regarding -night resource and keyboard by @bmarty in https://github.com/element-hq/element-x-android/pull/3216
|
||||||
|
* Fix rendering issue of SunsetPage in dark mode by @bmarty in https://github.com/element-hq/element-x-android/pull/3217
|
||||||
|
* Fix linkification not working for `Spanned` strings in text messages by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3233
|
||||||
|
* Edit : fallback to room.edit when timeline item is not found. by @ganfra in https://github.com/element-hq/element-x-android/pull/3239
|
||||||
|
|
||||||
|
### 🗣 Translations
|
||||||
|
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3156
|
||||||
|
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3192
|
||||||
|
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3232
|
||||||
|
|
||||||
|
### 🧱 Build
|
||||||
|
* Remove Showkase processor not found warning from Danger by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3148
|
||||||
|
* Set targetSDK to 34 by @bmarty in https://github.com/element-hq/element-x-android/pull/3149
|
||||||
|
* Add a local copy of `inplace-fix.py` and `fix-pg-map-id.py` by @bmarty in https://github.com/element-hq/element-x-android/pull/3167
|
||||||
|
* Only add private SSH keys and clone submodules in the original repo by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3225
|
||||||
|
* Fix CI for forks by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3226
|
||||||
|
|
||||||
|
### Dependency upgrades
|
||||||
|
* Update dependency io.element.android:compound-android to v0.0.7 by @renovate in https://github.com/element-hq/element-x-android/pull/3143
|
||||||
|
* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.31 by @renovate in https://github.com/element-hq/element-x-android/pull/3145
|
||||||
|
* Update dependency com.squareup:kotlinpoet to v1.18.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3150
|
||||||
|
* Update dependency org.robolectric:robolectric to v4.13 by @renovate in https://github.com/element-hq/element-x-android/pull/3157
|
||||||
|
* Update plugin dependencycheck to v10.0.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3154
|
||||||
|
* Update wysiwyg to v2.37.5 by @renovate in https://github.com/element-hq/element-x-android/pull/3162
|
||||||
|
* Update plugin sonarqube to v5.1.0.4882 by @renovate in https://github.com/element-hq/element-x-android/pull/3139
|
||||||
|
* Update dependency org.jsoup:jsoup to v1.18.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3171
|
||||||
|
* Update dependency com.google.firebase:firebase-bom to v33.1.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3178
|
||||||
|
* Update telephoto to v0.12.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3191
|
||||||
|
* Update dependency com.google.truth:truth to v1.4.4 by @renovate in https://github.com/element-hq/element-x-android/pull/3187
|
||||||
|
* Update dependency com.squareup:kotlinpoet to v1.18.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3194
|
||||||
|
* Update dependency io.mockk:mockk to v1.13.12 by @renovate in https://github.com/element-hq/element-x-android/pull/3198
|
||||||
|
* Update dependency io.sentry:sentry-android to v7.12.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3200
|
||||||
|
* Update plugin dependencycheck to v10.0.3 by @renovate in https://github.com/element-hq/element-x-android/pull/3204
|
||||||
|
* Update dependency gradle to v8.9 by @renovate in https://github.com/element-hq/element-x-android/pull/3177
|
||||||
|
* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.32 by @renovate in https://github.com/element-hq/element-x-android/pull/3202
|
||||||
|
* Update coil to v2.7.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3210
|
||||||
|
* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.33 by @renovate in https://github.com/element-hq/element-x-android/pull/3220
|
||||||
|
* Update wysiwyg to v2.37.7 by @renovate in https://github.com/element-hq/element-x-android/pull/3218
|
||||||
|
* Update telephoto to v0.12.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3230
|
||||||
|
* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.34 by @renovate in https://github.com/element-hq/element-x-android/pull/3237
|
||||||
|
|
||||||
|
### Others
|
||||||
|
* Reduce delay when selecting room list filters by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3160
|
||||||
|
* Add `--alignment-preserved true` when signing APK for F-Droid. by @bmarty in https://github.com/element-hq/element-x-android/pull/3161
|
||||||
|
* Ensure that all callback plugins are invoked. by @bmarty in https://github.com/element-hq/element-x-android/pull/3146
|
||||||
|
* Add generated screen to show open source licenses in Gplay variant by @bmarty in https://github.com/element-hq/element-x-android/pull/3207
|
||||||
|
* Performance : improve time to open a room. by @ganfra in https://github.com/element-hq/element-x-android/pull/3186
|
||||||
|
* Add logging to help debug forced logout issues by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3208
|
||||||
|
* Use the right filename for log files so they're sorted in rageshakes by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3219
|
||||||
|
* Compose : add immutability to some Reaction classes by @ganfra in https://github.com/element-hq/element-x-android/pull/3224
|
||||||
|
* Fix stickers display text on room summary by @surakin in https://github.com/element-hq/element-x-android/pull/3221
|
||||||
|
* Rework FakeMatrixRoom so that it contains only lambdas. by @bmarty in https://github.com/element-hq/element-x-android/pull/3229
|
||||||
|
|
||||||
Changes in Element X v0.4.16 (2024-07-05)
|
Changes in Element X v0.4.16 (2024-07-05)
|
||||||
=========================================
|
=========================================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@
|
||||||
<locale android:name="in"/>
|
<locale android:name="in"/>
|
||||||
<locale android:name="it"/>
|
<locale android:name="it"/>
|
||||||
<locale android:name="ka"/>
|
<locale android:name="ka"/>
|
||||||
|
<locale android:name="pl"/>
|
||||||
<locale android:name="pt"/>
|
<locale android:name="pt"/>
|
||||||
|
<locale android:name="pt_BR"/>
|
||||||
<locale android:name="ro"/>
|
<locale android:name="ro"/>
|
||||||
<locale android:name="ru"/>
|
<locale android:name="ru"/>
|
||||||
<locale android:name="sk"/>
|
<locale android:name="sk"/>
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,9 @@ class JoinRoomLoadedFlowNodeTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given a room flow node when initialized then it loads messages entry point`() = runTest {
|
fun `given a room flow node when initialized then it loads messages entry point`() = runTest {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { }
|
||||||
|
)
|
||||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
|
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
|
||||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||||
|
|
@ -144,7 +146,9 @@ class JoinRoomLoadedFlowNodeTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest {
|
fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { }
|
||||||
|
)
|
||||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||||
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
||||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
|
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import org.junit.Test
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class) class SendQueuesTest {
|
@OptIn(ExperimentalCoroutinesApi::class) class SendQueuesTest {
|
||||||
private val matrixClient = FakeMatrixClient()
|
private val matrixClient = FakeMatrixClient()
|
||||||
private val room = FakeMatrixRoom()
|
|
||||||
private val networkMonitor = FakeNetworkMonitor()
|
private val networkMonitor = FakeNetworkMonitor()
|
||||||
private val sut = SendQueues(matrixClient, networkMonitor)
|
private val sut = SendQueues(matrixClient, networkMonitor)
|
||||||
|
|
||||||
|
|
@ -43,11 +42,11 @@ import org.junit.Test
|
||||||
val setAllSendQueuesEnabledLambda = lambdaRecorder { _: Boolean -> }
|
val setAllSendQueuesEnabledLambda = lambdaRecorder { _: Boolean -> }
|
||||||
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
|
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
|
||||||
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
|
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
|
||||||
matrixClient.givenGetRoomResult(room.roomId, room)
|
|
||||||
|
|
||||||
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
|
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
|
||||||
room.setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
val room = FakeMatrixRoom(
|
||||||
|
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
||||||
|
)
|
||||||
|
matrixClient.givenGetRoomResult(room.roomId, room)
|
||||||
sut.launchIn(backgroundScope)
|
sut.launchIn(backgroundScope)
|
||||||
|
|
||||||
sendQueueDisabledFlow.emit(room.roomId)
|
sendQueueDisabledFlow.emit(room.roomId)
|
||||||
|
|
@ -72,10 +71,11 @@ import org.junit.Test
|
||||||
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
|
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
|
||||||
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
|
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
|
||||||
networkMonitor.connectivity.value = NetworkStatus.Offline
|
networkMonitor.connectivity.value = NetworkStatus.Offline
|
||||||
matrixClient.givenGetRoomResult(room.roomId, room)
|
|
||||||
|
|
||||||
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
|
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
|
||||||
room.setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
val room = FakeMatrixRoom(
|
||||||
|
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
||||||
|
)
|
||||||
|
matrixClient.givenGetRoomResult(room.roomId, room)
|
||||||
|
|
||||||
sut.launchIn(backgroundScope)
|
sut.launchIn(backgroundScope)
|
||||||
|
|
||||||
|
|
|
||||||
2
fastlane/metadata/android/en-US/changelogs/40005000.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40005000.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: mostly bug fixes and performance improvements.
|
||||||
|
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_analytics_settings_help_us_improve">"Udostępniaj anonimowe dane dotyczące użytkowania, aby pomóc nam identyfikować problemy."</string>
|
||||||
|
<string name="screen_analytics_settings_read_terms">"Możesz przeczytać wszystkie nasze warunki %1$s."</string>
|
||||||
|
<string name="screen_analytics_settings_read_terms_content_link">"tutaj"</string>
|
||||||
|
<string name="screen_analytics_settings_share_data">"Udostępniaj dane analityczne"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_analytics_settings_help_us_improve">"Compartilhe dados de uso anônimos para nos ajudar a identificar problemas."</string>
|
||||||
|
<string name="screen_analytics_settings_read_terms">"Você pode ler todos os nossos termos %1$s ."</string>
|
||||||
|
<string name="screen_analytics_settings_read_terms_content_link">"aqui"</string>
|
||||||
|
<string name="screen_analytics_settings_share_data">"Compartilhar dados de utilização"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_analytics_prompt_data_usage">"Nie będziemy rejestrować ani profilować żadnych danych osobistych"</string>
|
||||||
|
<string name="screen_analytics_prompt_help_us_improve">"Udostępniaj anonimowe dane dotyczące użytkowania, aby pomóc nam identyfikować problemy."</string>
|
||||||
|
<string name="screen_analytics_prompt_read_terms">"Możesz przeczytać wszystkie nasze warunki %1$s."</string>
|
||||||
|
<string name="screen_analytics_prompt_read_terms_content_link">"tutaj"</string>
|
||||||
|
<string name="screen_analytics_prompt_settings">"Możesz to wyłączyć w dowolnym momencie"</string>
|
||||||
|
<string name="screen_analytics_prompt_third_party_sharing">"Nie będziemy udostępniać Twoich danych podmiotom trzecim"</string>
|
||||||
|
<string name="screen_analytics_prompt_title">"Pomóż nam ulepszyć %1$s"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_analytics_prompt_data_usage">"Não registraremos nem criaremos perfil baseado em qualquer dado pessoal"</string>
|
||||||
|
<string name="screen_analytics_prompt_help_us_improve">"Compartilhe dados de uso anônimos para nos ajudar a identificar problemas."</string>
|
||||||
|
<string name="screen_analytics_prompt_read_terms">"Você pode ler todos os nossos termos %1$s ."</string>
|
||||||
|
<string name="screen_analytics_prompt_read_terms_content_link">"aqui"</string>
|
||||||
|
<string name="screen_analytics_prompt_settings">"Você pode desativar isso a qualquer momento"</string>
|
||||||
|
<string name="screen_analytics_prompt_third_party_sharing">"Não compartilharemos seus dados com terceiros"</string>
|
||||||
|
<string name="screen_analytics_prompt_title">"Ajude a melhorar o %1$s"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -25,14 +25,25 @@ import io.element.android.features.call.impl.notifications.CallNotificationData
|
||||||
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
|
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.SingleIn
|
import io.element.android.libraries.di.SingleIn
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||||
import io.element.android.libraries.push.api.notifications.ForegroundServiceType
|
import io.element.android.libraries.push.api.notifications.ForegroundServiceType
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
||||||
import io.element.android.libraries.push.api.notifications.OnMissedCallNotificationHandler
|
import io.element.android.libraries.push.api.notifications.OnMissedCallNotificationHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.drop
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -79,11 +90,16 @@ class DefaultActiveCallManager @Inject constructor(
|
||||||
private val onMissedCallNotificationHandler: OnMissedCallNotificationHandler,
|
private val onMissedCallNotificationHandler: OnMissedCallNotificationHandler,
|
||||||
private val ringingCallNotificationCreator: RingingCallNotificationCreator,
|
private val ringingCallNotificationCreator: RingingCallNotificationCreator,
|
||||||
private val notificationManagerCompat: NotificationManagerCompat,
|
private val notificationManagerCompat: NotificationManagerCompat,
|
||||||
|
private val matrixClientProvider: MatrixClientProvider,
|
||||||
) : ActiveCallManager {
|
) : ActiveCallManager {
|
||||||
private var timedOutCallJob: Job? = null
|
private var timedOutCallJob: Job? = null
|
||||||
|
|
||||||
override val activeCall = MutableStateFlow<ActiveCall?>(null)
|
override val activeCall = MutableStateFlow<ActiveCall?>(null)
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeRingingCall()
|
||||||
|
}
|
||||||
|
|
||||||
override fun registerIncomingCall(notificationData: CallNotificationData) {
|
override fun registerIncomingCall(notificationData: CallNotificationData) {
|
||||||
if (activeCall.value != null) {
|
if (activeCall.value != null) {
|
||||||
displayMissedCallNotification(notificationData)
|
displayMissedCallNotification(notificationData)
|
||||||
|
|
@ -173,6 +189,35 @@ class DefaultActiveCallManager @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
private fun observeRingingCall() {
|
||||||
|
// This will observe ringing calls and ensure they're terminated if the room call is cancelled
|
||||||
|
activeCall
|
||||||
|
.filterNotNull()
|
||||||
|
.filter { it.callState is CallState.Ringing && it.callType is CallType.RoomCall }
|
||||||
|
.flatMapLatest { activeCall ->
|
||||||
|
val callType = activeCall.callType as CallType.RoomCall
|
||||||
|
// Get a flow of updated `hasRoomCall` values for the room
|
||||||
|
matrixClientProvider.getOrRestore(callType.sessionId).getOrNull()
|
||||||
|
?.getRoom(callType.roomId)
|
||||||
|
?.roomInfoFlow
|
||||||
|
?.map { it.hasRoomCall }
|
||||||
|
?: flowOf()
|
||||||
|
}
|
||||||
|
// We only want to check if the room active call status changes
|
||||||
|
.distinctUntilChanged()
|
||||||
|
// Skip the first one, we're not interested in it (if the check below passes, it had to be active anyway)
|
||||||
|
.drop(1)
|
||||||
|
.onEach { roomHasActiveCall ->
|
||||||
|
if (!roomHasActiveCall) {
|
||||||
|
// The call was cancelled
|
||||||
|
timedOutCallJob?.cancel()
|
||||||
|
incomingCallTimedOut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launchIn(coroutineScope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@
|
||||||
<string name="call_foreground_service_channel_title_android">"Laufender Anruf"</string>
|
<string name="call_foreground_service_channel_title_android">"Laufender Anruf"</string>
|
||||||
<string name="call_foreground_service_message_android">"Tippen, um zum Anruf zurückzukehren"</string>
|
<string name="call_foreground_service_message_android">"Tippen, um zum Anruf zurückzukehren"</string>
|
||||||
<string name="call_foreground_service_title_android">"☎️ Anruf läuft"</string>
|
<string name="call_foreground_service_title_android">"☎️ Anruf läuft"</string>
|
||||||
|
<string name="screen_incoming_call_subtitle_android">"Eingehender Element Call"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="call_foreground_service_channel_title_android">"Połączenie w trakcie"</string>
|
||||||
|
<string name="call_foreground_service_message_android">"Stuknij, aby wrócić do rozmowy"</string>
|
||||||
|
<string name="call_foreground_service_title_android">"☎️ Rozmowa w toku"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="call_foreground_service_channel_title_android">"Chamada em andamento"</string>
|
||||||
|
<string name="call_foreground_service_message_android">"Toque para retornar à chamada"</string>
|
||||||
|
<string name="call_foreground_service_title_android">"☎️ Chamada em andamento"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -32,7 +32,10 @@ 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_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_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.FakeMatrixClientProvider
|
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||||
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.push.api.notifications.ForegroundServiceType
|
import io.element.android.libraries.push.api.notifications.ForegroundServiceType
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
|
||||||
import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder
|
import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder
|
||||||
|
|
@ -42,7 +45,11 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.lambda.value
|
import io.element.android.tests.testutils.lambda.value
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.advanceTimeBy
|
import kotlinx.coroutines.test.advanceTimeBy
|
||||||
import kotlinx.coroutines.test.runCurrent
|
import kotlinx.coroutines.test.runCurrent
|
||||||
|
|
@ -59,26 +66,28 @@ class DefaultActiveCallManagerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `registerIncomingCall - sets the incoming call as active`() = runTest {
|
fun `registerIncomingCall - sets the incoming call as active`() = runTest {
|
||||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||||
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
inCancellableScope {
|
||||||
|
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||||
|
|
||||||
assertThat(manager.activeCall.value).isNull()
|
assertThat(manager.activeCall.value).isNull()
|
||||||
|
|
||||||
val callNotificationData = aCallNotificationData()
|
val callNotificationData = aCallNotificationData()
|
||||||
manager.registerIncomingCall(callNotificationData)
|
manager.registerIncomingCall(callNotificationData)
|
||||||
|
|
||||||
assertThat(manager.activeCall.value).isEqualTo(
|
assertThat(manager.activeCall.value).isEqualTo(
|
||||||
ActiveCall(
|
ActiveCall(
|
||||||
callType = CallType.RoomCall(
|
callType = CallType.RoomCall(
|
||||||
sessionId = callNotificationData.sessionId,
|
sessionId = callNotificationData.sessionId,
|
||||||
roomId = callNotificationData.roomId,
|
roomId = callNotificationData.roomId,
|
||||||
),
|
),
|
||||||
callState = CallState.Ringing(callNotificationData)
|
callState = CallState.Ringing(callNotificationData)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
verify { notificationManagerCompat.notify(notificationId, any()) }
|
verify { notificationManagerCompat.notify(notificationId, any()) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
@ -86,38 +95,42 @@ class DefaultActiveCallManagerTest {
|
||||||
fun `registerIncomingCall - when there is an already active call adds missed call notification`() = runTest {
|
fun `registerIncomingCall - when there is an already active call adds missed call notification`() = runTest {
|
||||||
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
||||||
val onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
val onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
||||||
val manager = createActiveCallManager(
|
inCancellableScope {
|
||||||
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
val manager = createActiveCallManager(
|
||||||
)
|
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
||||||
|
)
|
||||||
|
|
||||||
// Register existing call
|
// Register existing call
|
||||||
val callNotificationData = aCallNotificationData()
|
val callNotificationData = aCallNotificationData()
|
||||||
manager.registerIncomingCall(callNotificationData)
|
manager.registerIncomingCall(callNotificationData)
|
||||||
val activeCall = manager.activeCall.value
|
val activeCall = manager.activeCall.value
|
||||||
|
|
||||||
// Now add a new call
|
// Now add a new call
|
||||||
manager.registerIncomingCall(aCallNotificationData(roomId = A_ROOM_ID_2))
|
manager.registerIncomingCall(aCallNotificationData(roomId = A_ROOM_ID_2))
|
||||||
|
|
||||||
assertThat(manager.activeCall.value).isEqualTo(activeCall)
|
assertThat(manager.activeCall.value).isEqualTo(activeCall)
|
||||||
assertThat((manager.activeCall.value?.callType as? CallType.RoomCall)?.roomId).isNotEqualTo(A_ROOM_ID_2)
|
assertThat((manager.activeCall.value?.callType as? CallType.RoomCall)?.roomId).isNotEqualTo(A_ROOM_ID_2)
|
||||||
|
|
||||||
advanceTimeBy(1)
|
advanceTimeBy(1)
|
||||||
|
|
||||||
addMissedCallNotificationLambda.assertions()
|
addMissedCallNotificationLambda.assertions()
|
||||||
.isCalledOnce()
|
.isCalledOnce()
|
||||||
.with(value(A_SESSION_ID), value(A_ROOM_ID_2), value(AN_EVENT_ID))
|
.with(value(A_SESSION_ID), value(A_ROOM_ID_2), value(AN_EVENT_ID))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `incomingCallTimedOut - when there isn't an active call does nothing`() = runTest {
|
fun `incomingCallTimedOut - when there isn't an active call does nothing`() = runTest {
|
||||||
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
||||||
val manager = createActiveCallManager(
|
inCancellableScope {
|
||||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
val manager = createActiveCallManager(
|
||||||
)
|
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
||||||
|
)
|
||||||
|
|
||||||
manager.incomingCallTimedOut()
|
manager.incomingCallTimedOut()
|
||||||
|
|
||||||
addMissedCallNotificationLambda.assertions().isNeverCalled()
|
addMissedCallNotificationLambda.assertions().isNeverCalled()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
@ -125,82 +138,167 @@ class DefaultActiveCallManagerTest {
|
||||||
fun `incomingCallTimedOut - when there is an active call removes it and adds a missed call notification`() = runTest {
|
fun `incomingCallTimedOut - when there is an active call removes it and adds a missed call notification`() = runTest {
|
||||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||||
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
||||||
val manager = createActiveCallManager(
|
inCancellableScope {
|
||||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda),
|
val manager = createActiveCallManager(
|
||||||
notificationManagerCompat = notificationManagerCompat,
|
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda),
|
||||||
)
|
notificationManagerCompat = notificationManagerCompat,
|
||||||
|
)
|
||||||
|
|
||||||
manager.registerIncomingCall(aCallNotificationData())
|
manager.registerIncomingCall(aCallNotificationData())
|
||||||
assertThat(manager.activeCall.value).isNotNull()
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
|
||||||
manager.incomingCallTimedOut()
|
manager.incomingCallTimedOut()
|
||||||
advanceTimeBy(1)
|
advanceTimeBy(1)
|
||||||
|
|
||||||
assertThat(manager.activeCall.value).isNull()
|
assertThat(manager.activeCall.value).isNull()
|
||||||
addMissedCallNotificationLambda.assertions().isCalledOnce()
|
addMissedCallNotificationLambda.assertions().isCalledOnce()
|
||||||
verify { notificationManagerCompat.cancel(notificationId) }
|
verify { notificationManagerCompat.cancel(notificationId) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `hungUpCall - removes existing call if the CallType matches`() = runTest {
|
fun `hungUpCall - removes existing call if the CallType matches`() = runTest {
|
||||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||||
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
// Create a cancellable coroutine scope to cancel the test when needed
|
||||||
|
inCancellableScope {
|
||||||
|
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||||
|
|
||||||
val notificationData = aCallNotificationData()
|
val notificationData = aCallNotificationData()
|
||||||
manager.registerIncomingCall(notificationData)
|
manager.registerIncomingCall(notificationData)
|
||||||
assertThat(manager.activeCall.value).isNotNull()
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
|
||||||
manager.hungUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
manager.hungUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
||||||
assertThat(manager.activeCall.value).isNull()
|
assertThat(manager.activeCall.value).isNull()
|
||||||
|
|
||||||
verify { notificationManagerCompat.cancel(notificationId) }
|
verify { notificationManagerCompat.cancel(notificationId) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `hungUpCall - does nothing if the CallType doesn't match`() = runTest {
|
fun `hungUpCall - does nothing if the CallType doesn't match`() = runTest {
|
||||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||||
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
// Create a cancellable coroutine scope to cancel the test when needed
|
||||||
|
inCancellableScope {
|
||||||
|
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||||
|
|
||||||
manager.registerIncomingCall(aCallNotificationData())
|
manager.registerIncomingCall(aCallNotificationData())
|
||||||
assertThat(manager.activeCall.value).isNotNull()
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
|
||||||
manager.hungUpCall(CallType.ExternalUrl("https://example.com"))
|
manager.hungUpCall(CallType.ExternalUrl("https://example.com"))
|
||||||
assertThat(manager.activeCall.value).isNotNull()
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
|
||||||
verify(exactly = 0) { notificationManagerCompat.cancel(notificationId) }
|
verify(exactly = 0) { notificationManagerCompat.cancel(notificationId) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Test
|
@Test
|
||||||
fun `joinedCall - register an ongoing call and tries sending the call notify event`() = runTest {
|
fun `joinedCall - register an ongoing call and tries sending the call notify event`() = runTest {
|
||||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||||
val manager = createActiveCallManager(
|
inCancellableScope {
|
||||||
notificationManagerCompat = notificationManagerCompat,
|
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||||
)
|
assertThat(manager.activeCall.value).isNull()
|
||||||
assertThat(manager.activeCall.value).isNull()
|
|
||||||
|
|
||||||
manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID))
|
manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID))
|
||||||
assertThat(manager.activeCall.value).isEqualTo(
|
assertThat(manager.activeCall.value).isEqualTo(
|
||||||
ActiveCall(
|
ActiveCall(
|
||||||
callType = CallType.RoomCall(
|
callType = CallType.RoomCall(
|
||||||
sessionId = A_SESSION_ID,
|
sessionId = A_SESSION_ID,
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
),
|
),
|
||||||
callState = CallState.InCall,
|
callState = CallState.InCall,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
verify { notificationManagerCompat.cancel(notificationId) }
|
verify { notificationManagerCompat.cancel(notificationId) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createActiveCallManager(
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@Test
|
||||||
|
fun `observeRingingCalls - will cancel the active ringing call if the call is cancelled`() = runTest {
|
||||||
|
val room = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo())
|
||||||
|
}
|
||||||
|
val client = FakeMatrixClient().apply {
|
||||||
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
|
}
|
||||||
|
// Create a cancellable coroutine scope to cancel the test when needed
|
||||||
|
inCancellableScope {
|
||||||
|
val matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(client) })
|
||||||
|
val manager = createActiveCallManager(matrixClientProvider = matrixClientProvider)
|
||||||
|
|
||||||
|
manager.registerIncomingCall(aCallNotificationData())
|
||||||
|
|
||||||
|
// Call is active (the other user join the call)
|
||||||
|
room.givenRoomInfo(aRoomInfo(hasRoomCall = true))
|
||||||
|
advanceTimeBy(1)
|
||||||
|
// Call is cancelled (the other user left the call)
|
||||||
|
room.givenRoomInfo(aRoomInfo(hasRoomCall = false))
|
||||||
|
advanceTimeBy(1)
|
||||||
|
|
||||||
|
assertThat(manager.activeCall.value).isNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@Test
|
||||||
|
fun `observeRingingCalls - will do nothing if either the session or the room are not found`() = runTest {
|
||||||
|
val room = FakeMatrixRoom().apply {
|
||||||
|
givenRoomInfo(aRoomInfo())
|
||||||
|
}
|
||||||
|
val client = FakeMatrixClient().apply {
|
||||||
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
|
}
|
||||||
|
// Create a cancellable coroutine scope to cancel the test when needed
|
||||||
|
inCancellableScope {
|
||||||
|
val matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.failure(IllegalStateException("Matrix client not found")) })
|
||||||
|
val manager = createActiveCallManager(matrixClientProvider = matrixClientProvider)
|
||||||
|
|
||||||
|
// No matrix client
|
||||||
|
|
||||||
|
manager.registerIncomingCall(aCallNotificationData())
|
||||||
|
|
||||||
|
room.givenRoomInfo(aRoomInfo(hasRoomCall = true))
|
||||||
|
advanceTimeBy(1)
|
||||||
|
room.givenRoomInfo(aRoomInfo(hasRoomCall = false))
|
||||||
|
advanceTimeBy(1)
|
||||||
|
|
||||||
|
// The call should still be active
|
||||||
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
|
||||||
|
// No room
|
||||||
|
client.givenGetRoomResult(A_ROOM_ID, null)
|
||||||
|
matrixClientProvider.getClient = { Result.success(client) }
|
||||||
|
|
||||||
|
manager.registerIncomingCall(aCallNotificationData())
|
||||||
|
|
||||||
|
room.givenRoomInfo(aRoomInfo(hasRoomCall = true))
|
||||||
|
advanceTimeBy(1)
|
||||||
|
room.givenRoomInfo(aRoomInfo(hasRoomCall = false))
|
||||||
|
advanceTimeBy(1)
|
||||||
|
|
||||||
|
// The call should still be active
|
||||||
|
assertThat(manager.activeCall.value).isNotNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.inCancellableScope(block: suspend CoroutineScope.() -> Unit) {
|
||||||
|
launch(SupervisorJob()) {
|
||||||
|
block()
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.createActiveCallManager(
|
||||||
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
|
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
|
||||||
onMissedCallNotificationHandler: FakeOnMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(),
|
onMissedCallNotificationHandler: FakeOnMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(),
|
||||||
notificationManagerCompat: NotificationManagerCompat = mockk(relaxed = true),
|
notificationManagerCompat: NotificationManagerCompat = mockk(relaxed = true),
|
||||||
|
coroutineScope: CoroutineScope = this,
|
||||||
) = DefaultActiveCallManager(
|
) = DefaultActiveCallManager(
|
||||||
coroutineScope = this,
|
coroutineScope = coroutineScope,
|
||||||
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
||||||
ringingCallNotificationCreator = RingingCallNotificationCreator(
|
ringingCallNotificationCreator = RingingCallNotificationCreator(
|
||||||
context = InstrumentationRegistry.getInstrumentation().targetContext,
|
context = InstrumentationRegistry.getInstrumentation().targetContext,
|
||||||
|
|
@ -209,5 +307,6 @@ class DefaultActiveCallManagerTest {
|
||||||
notificationBitmapLoader = FakeNotificationBitmapLoader(),
|
notificationBitmapLoader = FakeNotificationBitmapLoader(),
|
||||||
),
|
),
|
||||||
notificationManagerCompat = notificationManagerCompat,
|
notificationManagerCompat = notificationManagerCompat,
|
||||||
|
matrixClientProvider = matrixClientProvider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,9 @@ class DefaultCallWidgetProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getWidget - fails if it can't generate the URL for the widget`() = runTest {
|
fun `getWidget - fails if it can't generate the URL for the widget`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenGenerateWidgetWebViewUrlResult(Result.failure(Exception("Can't generate URL for widget")))
|
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.failure(Exception("Can't generate URL for widget")) }
|
||||||
}
|
)
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
}
|
}
|
||||||
|
|
@ -66,10 +66,10 @@ class DefaultCallWidgetProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getWidget - fails if it can't get the widget driver`() = runTest {
|
fun `getWidget - fails if it can't get the widget driver`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||||
givenGetWidgetDriverResult(Result.failure(Exception("Can't get a widget driver")))
|
getWidgetDriverResult = { Result.failure(Exception("Can't get a widget driver")) }
|
||||||
}
|
)
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
}
|
}
|
||||||
|
|
@ -79,10 +79,10 @@ class DefaultCallWidgetProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getWidget - returns a widget driver when all steps are successful`() = runTest {
|
fun `getWidget - returns a widget driver when all steps are successful`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||||
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
|
getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
|
||||||
}
|
)
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
}
|
}
|
||||||
|
|
@ -92,10 +92,10 @@ class DefaultCallWidgetProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getWidget - will use a custom base url if it exists`() = runTest {
|
fun `getWidget - will use a custom base url if it exists`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||||
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
|
getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
|
||||||
}
|
)
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
}
|
}
|
||||||
|
|
@ -120,10 +120,10 @@ class DefaultCallWidgetProviderTest {
|
||||||
val elementCallBaseUrlProvider = FakeElementCallBaseUrlProvider { matrixClient ->
|
val elementCallBaseUrlProvider = FakeElementCallBaseUrlProvider { matrixClient ->
|
||||||
providesLambda(matrixClient)
|
providesLambda(matrixClient)
|
||||||
}
|
}
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||||
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
|
getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
|
||||||
}
|
)
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_create_room_action_create_room">"Nowy pokój"</string>
|
||||||
|
<string name="screen_create_room_add_people_title">"Zaproś znajomych"</string>
|
||||||
|
<string name="screen_create_room_error_creating_room">"Wystąpił błąd podczas tworzenia pokoju"</string>
|
||||||
|
<string name="screen_create_room_private_option_description">"Wiadomości w tym pokoju są szyfrowane. Szyfrowania nie można później wyłączyć."</string>
|
||||||
|
<string name="screen_create_room_private_option_title">"Pokój prywatny (tylko zaproszenie)"</string>
|
||||||
|
<string name="screen_create_room_public_option_description">"Wiadomości nie są szyfrowane i każdy może je odczytać. Możesz aktywować szyfrowanie później."</string>
|
||||||
|
<string name="screen_create_room_public_option_title">"Pokój publiczny (każdy)"</string>
|
||||||
|
<string name="screen_create_room_room_name_label">"Nazwa pokoju"</string>
|
||||||
|
<string name="screen_create_room_title">"Utwórz pokój"</string>
|
||||||
|
<string name="screen_create_room_topic_label">"Temat (opcjonalnie)"</string>
|
||||||
|
<string name="screen_start_chat_error_starting_chat">"Wystąpił błąd podczas próby rozpoczęcia czatu"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_create_room_action_create_room">"Nova sala"</string>
|
||||||
|
<string name="screen_create_room_add_people_title">"Convidar pessoas"</string>
|
||||||
|
<string name="screen_create_room_error_creating_room">"Ocorreu um erro ao criar a sala"</string>
|
||||||
|
<string name="screen_create_room_private_option_description">"As mensagens nesta sala serão criptografadas. A criptografia não pode ser desativada posteriormente."</string>
|
||||||
|
<string name="screen_create_room_private_option_title">"Sala privativa (somente por convite)"</string>
|
||||||
|
<string name="screen_create_room_public_option_description">"As mensagens não serão criptografadas e qualquer pessoa pode lê-las. Você pode ativar a criptografia posteriormente."</string>
|
||||||
|
<string name="screen_create_room_public_option_title">"Sala pública (qualquer pessoa)"</string>
|
||||||
|
<string name="screen_create_room_room_name_label">"Nome da sala"</string>
|
||||||
|
<string name="screen_create_room_title">"Criar uma sala"</string>
|
||||||
|
<string name="screen_create_room_topic_label">"Tópico (opcional)"</string>
|
||||||
|
<string name="screen_start_chat_error_starting_chat">"Ocorreu um erro ao tentar iniciar um chat"</string>
|
||||||
|
</resources>
|
||||||
11
features/ftue/impl/src/main/res/values-pl/translations.xml
Normal file
11
features/ftue/impl/src/main/res/values-pl/translations.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_notification_optin_subtitle">"Możesz zmienić ustawienia później."</string>
|
||||||
|
<string name="screen_notification_optin_title">"Zezwól na powiadomienia i nie przegap żadnej wiadomości"</string>
|
||||||
|
<string name="screen_welcome_bullet_1">"Połączenia, ankiety, wyszukiwanie i inne zostaną dodane później w tym roku."</string>
|
||||||
|
<string name="screen_welcome_bullet_2">"Historia wiadomości dla pokoi szyfrowanych nie jest jeszcze dostępna."</string>
|
||||||
|
<string name="screen_welcome_bullet_3">"Chętnie poznamy Twoją opinię. Daj nam znać, co myślisz na stronie ustawień."</string>
|
||||||
|
<string name="screen_welcome_button">"Naprzód!"</string>
|
||||||
|
<string name="screen_welcome_subtitle">"Oto, co musisz wiedzieć:"</string>
|
||||||
|
<string name="screen_welcome_title">"Witamy w %1$s!"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_notification_optin_subtitle">"Você pode alterar suas configurações mais tarde."</string>
|
||||||
|
<string name="screen_notification_optin_title">"Permita notificações e nunca perca uma mensagem"</string>
|
||||||
|
<string name="screen_welcome_bullet_1">"Chamadas, enquetes, pesquisa e muito mais serão adicionadas ainda este ano."</string>
|
||||||
|
<string name="screen_welcome_bullet_2">"O histórico de mensagens para salas criptografadas ainda não está disponível."</string>
|
||||||
|
<string name="screen_welcome_bullet_3">"Adoraríamos ouvir sua opinião. Deixe-nos saber o que você pensa através da página de configurações."</string>
|
||||||
|
<string name="screen_welcome_button">"Vamos lá!"</string>
|
||||||
|
<string name="screen_welcome_subtitle">"Aqui está o que você precisa saber:"</string>
|
||||||
|
<string name="screen_welcome_title">"Bem-vindo ao %1$s!"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie do dołączenia do %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string>
|
||||||
|
<string name="screen_invites_empty_list">"Brak zaproszeń"</string>
|
||||||
|
<string name="screen_invites_invited_you">"%1$s (%2$s) zaprosił Cię"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_invites_decline_chat_message">"Tem certeza de que deseja recusar o convite para ingressar em %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_chat_title">"Recusar convite"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_message">"Tem certeza de que deseja recusar esse chat privado com %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_title">"Recusar chat"</string>
|
||||||
|
<string name="screen_invites_empty_list">"Sem convites"</string>
|
||||||
|
<string name="screen_invites_invited_you">"%1$s(%2$s) convidou você"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -92,9 +92,9 @@ class AcceptDeclineInvitePresenterTest {
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom().apply {
|
result = FakeMatrixRoom(
|
||||||
leaveRoomLambda = declineInviteFailure
|
leaveRoomLambda = declineInviteFailure
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||||
|
|
@ -142,9 +142,9 @@ class AcceptDeclineInvitePresenterTest {
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom().apply {
|
result = FakeMatrixRoom(
|
||||||
leaveRoomLambda = declineInviteSuccess
|
leaveRoomLambda = declineInviteSuccess
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val presenter = createAcceptDeclineInvitePresenter(
|
val presenter = createAcceptDeclineInvitePresenter(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="leave_conversation_alert_subtitle">"Czy na pewno chcesz opuścić tę konwersację? Konwersacja nie jest publiczna i nie będziesz mógł dołączyć ponownie bez zaproszenia."</string>
|
||||||
|
<string name="leave_room_alert_empty_subtitle">"Jesteś pewien, że chcesz opuścić ten pokój? Jesteś tu jedyną osobą. Jeśli wyjdziesz, nikt nie będzie mógł dołączyć, w tym Ty."</string>
|
||||||
|
<string name="leave_room_alert_private_subtitle">"Czy na pewno chcesz opuścić ten pokój? Ten pokój nie jest publiczny i nie będziesz mógł do niego wrócić bez zaproszenia."</string>
|
||||||
|
<string name="leave_room_alert_subtitle">"Jesteś pewien, że chcesz wyjść z tego pokoju?"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="leave_room_alert_empty_subtitle">"Tem certeza de que deseja sair desta sala? Você é a única pessoa aqui. Se você sair, ninguém poderá ingressar no futuro, inclusive você."</string>
|
||||||
|
<string name="leave_room_alert_private_subtitle">"Tem certeza de que deseja sair desta sala? Esta sala não é pública e você não poderá entrar novamente sem um convite."</string>
|
||||||
|
<string name="leave_room_alert_subtitle">"Tem certeza de que deseja sair da sala?"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -140,7 +140,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom(),
|
result = FakeMatrixRoom(
|
||||||
|
leaveRoomLambda = { Result.success(Unit) }
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
roomMembershipObserver = roomMembershipObserver
|
roomMembershipObserver = roomMembershipObserver
|
||||||
|
|
@ -162,9 +164,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom().apply {
|
result = FakeMatrixRoom(
|
||||||
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
||||||
},
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -186,7 +188,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom(),
|
result = FakeMatrixRoom(
|
||||||
|
leaveRoomLambda = { Result.success(Unit) }
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -208,9 +212,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||||
client = FakeMatrixClient().apply {
|
client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(
|
givenGetRoomResult(
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
result = FakeMatrixRoom().apply {
|
result = FakeMatrixRoom(
|
||||||
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
||||||
},
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,15 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE
|
||||||
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
|
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
|
||||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||||
import io.element.android.features.messages.test.FakeMessageComposerContext
|
import io.element.android.features.messages.test.FakeMessageComposerContext
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
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.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.SendLocationInvocation
|
|
||||||
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
import io.element.android.tests.testutils.lambda.value
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
@ -46,16 +48,18 @@ class SendLocationPresenterTest {
|
||||||
val warmUpRule = WarmUpRule()
|
val warmUpRule = WarmUpRule()
|
||||||
|
|
||||||
private val fakePermissionsPresenter = FakePermissionsPresenter()
|
private val fakePermissionsPresenter = FakePermissionsPresenter()
|
||||||
private val fakeMatrixRoom = FakeMatrixRoom()
|
|
||||||
private val fakeAnalyticsService = FakeAnalyticsService()
|
private val fakeAnalyticsService = FakeAnalyticsService()
|
||||||
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
||||||
private val fakeLocationActions = FakeLocationActions()
|
private val fakeLocationActions = FakeLocationActions()
|
||||||
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
||||||
private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter(
|
|
||||||
|
private fun createSendLocationPresenter(
|
||||||
|
matrixRoom: MatrixRoom = FakeMatrixRoom(),
|
||||||
|
): SendLocationPresenter = SendLocationPresenter(
|
||||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||||
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
||||||
},
|
},
|
||||||
room = fakeMatrixRoom,
|
room = matrixRoom,
|
||||||
analyticsService = fakeAnalyticsService,
|
analyticsService = fakeAnalyticsService,
|
||||||
messageComposerContext = fakeMessageComposerContext,
|
messageComposerContext = fakeMessageComposerContext,
|
||||||
locationActions = fakeLocationActions,
|
locationActions = fakeLocationActions,
|
||||||
|
|
@ -64,6 +68,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions granted`() = runTest {
|
fun `initial state with permissions granted`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.AllGranted,
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
|
@ -90,6 +95,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions partially granted`() = runTest {
|
fun `initial state with permissions partially granted`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.SomeGranted,
|
permissions = PermissionsState.Permissions.SomeGranted,
|
||||||
|
|
@ -116,6 +122,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions denied`() = runTest {
|
fun `initial state with permissions denied`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -142,6 +149,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions denied once`() = runTest {
|
fun `initial state with permissions denied once`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -168,6 +176,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `rationale dialog dismiss`() = runTest {
|
fun `rationale dialog dismiss`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -199,6 +208,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `rationale dialog continue`() = runTest {
|
fun `rationale dialog continue`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -227,6 +237,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `permission denied dialog dismiss`() = runTest {
|
fun `permission denied dialog dismiss`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -258,6 +269,13 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `share sender location`() = runTest {
|
fun `share sender location`() = runTest {
|
||||||
|
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
sendLocationResult = sendLocationResult,
|
||||||
|
)
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter(matrixRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.AllGranted,
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
|
@ -289,16 +307,14 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
delay(1) // Wait for the coroutine to finish
|
delay(1) // Wait for the coroutine to finish
|
||||||
|
|
||||||
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
|
sendLocationResult.assertions().isCalledOnce()
|
||||||
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
|
.with(
|
||||||
SendLocationInvocation(
|
value("Location was shared at geo:3.0,4.0;u=5.0"),
|
||||||
body = "Location was shared at geo:3.0,4.0;u=5.0",
|
value("geo:3.0,4.0;u=5.0"),
|
||||||
geoUri = "geo:3.0,4.0;u=5.0",
|
value(null),
|
||||||
description = null,
|
value(15),
|
||||||
zoomLevel = 15,
|
value(AssetType.SENDER),
|
||||||
assetType = AssetType.SENDER
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
||||||
|
|
@ -314,6 +330,13 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `share pin location`() = runTest {
|
fun `share pin location`() = runTest {
|
||||||
|
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
sendLocationResult = sendLocationResult,
|
||||||
|
)
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter(matrixRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -345,16 +368,14 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
delay(1) // Wait for the coroutine to finish
|
delay(1) // Wait for the coroutine to finish
|
||||||
|
|
||||||
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
|
sendLocationResult.assertions().isCalledOnce()
|
||||||
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
|
.with(
|
||||||
SendLocationInvocation(
|
value("Location was shared at geo:0.0,1.0"),
|
||||||
body = "Location was shared at geo:0.0,1.0",
|
value("geo:0.0,1.0"),
|
||||||
geoUri = "geo:0.0,1.0",
|
value(null),
|
||||||
description = null,
|
value(15),
|
||||||
zoomLevel = 15,
|
value(AssetType.PIN),
|
||||||
assetType = AssetType.PIN
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
||||||
|
|
@ -370,6 +391,13 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `composer context passes through analytics`() = runTest {
|
fun `composer context passes through analytics`() = runTest {
|
||||||
|
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, Result<Unit>> { _, _, _, _, _ ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
sendLocationResult = sendLocationResult,
|
||||||
|
)
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter(matrixRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -418,6 +446,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `open settings activity`() = runTest {
|
fun `open settings activity`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -452,6 +481,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `application name is in state`() = runTest {
|
fun `application name is in state`() = runTest {
|
||||||
|
val sendLocationPresenter = createSendLocationPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
sendLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,26 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_app_lock_biometric_authentication">"ბიომეტრიული ავტორიზაცია"</string>
|
||||||
|
<string name="screen_app_lock_biometric_unlock">"ბიომეტრიული განბლოკვა"</string>
|
||||||
|
<string name="screen_app_lock_biometric_unlock_title_android">"განბლოკვა ბიომეტრიით"</string>
|
||||||
<string name="screen_app_lock_forgot_pin">"დაგავიწყდათ PIN?"</string>
|
<string name="screen_app_lock_forgot_pin">"დაგავიწყდათ PIN?"</string>
|
||||||
<string name="screen_app_lock_settings_change_pin">"PIN კოდის შეცვლა"</string>
|
<string name="screen_app_lock_settings_change_pin">"PIN კოდის შეცვლა"</string>
|
||||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"ბიომეტრიული განბლოკვის დაშვება"</string>
|
<string name="screen_app_lock_settings_enable_biometric_unlock">"ბიომეტრიული განბლოკვის დაშვება"</string>
|
||||||
<string name="screen_app_lock_settings_remove_pin">"პინ კოდის წაშლა"</string>
|
<string name="screen_app_lock_settings_remove_pin">"პინ კოდის წაშლა"</string>
|
||||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"დარწმუნებული ხართ, რომ გსურთ PIN-ის წაშლა?"</string>
|
<string name="screen_app_lock_settings_remove_pin_alert_message">"დარწმუნებული ხართ, რომ გსურთ PIN-ის წაშლა?"</string>
|
||||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"გსურთ PIN-ის წაშლა?"</string>
|
<string name="screen_app_lock_settings_remove_pin_alert_title">"გსურთ PIN-ის წაშლა?"</string>
|
||||||
|
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"%1$s დაშვება"</string>
|
||||||
|
<string name="screen_app_lock_setup_biometric_unlock_skip">"მირჩევნია PIN-ის გამოყენება"</string>
|
||||||
|
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"დაზოგეთ დრო და გამოიყენეთ %1$s აპლიკაციის განსაბლოკად."</string>
|
||||||
|
<string name="screen_app_lock_setup_choose_pin">"აირჩიეთ PIN"</string>
|
||||||
<string name="screen_app_lock_setup_confirm_pin">"დაადასტურეთ PIN"</string>
|
<string name="screen_app_lock_setup_confirm_pin">"დაადასტურეთ PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_context">"თქვენი ჩატების დამატებითი უსაფრთხოებისათვის დაბლოკეთ %1$s.
|
||||||
|
|
||||||
|
აირჩიეთ რაიმე ისეთი, რაც დაგამახსოვრდებათ. თუ დაგავიწყდებათ ეს PIN, აპლიკაციიდან გამოხვალთ."</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"თქვენ არ შეგიძლიათ აირჩიოთ ეს PIN კოდი უსაფრთხოების მიზეზების გამო"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"აირჩიეთ სხვა PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"გთხოვთ შეიყვანოთ იგივე PIN ორჯერ"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PIN-ები არ ემთხვევა"</string>
|
||||||
<string name="screen_app_lock_signout_alert_message">"გასაგრძელებლად საჭიროა ხელახლა შესვლა და ახალი PIN-ის შექმნა"</string>
|
<string name="screen_app_lock_signout_alert_message">"გასაგრძელებლად საჭიროა ხელახლა შესვლა და ახალი PIN-ის შექმნა"</string>
|
||||||
<string name="screen_app_lock_signout_alert_title">"თქვენ ახლა გადიხართ…"</string>
|
<string name="screen_app_lock_signout_alert_title">"თქვენ ახლა გადიხართ…"</string>
|
||||||
<plurals name="screen_app_lock_subtitle">
|
<plurals name="screen_app_lock_subtitle">
|
||||||
|
|
@ -17,5 +31,7 @@
|
||||||
<item quantity="one">"არასწორი PIN. თქვენ %1$d შანსი დაგრჩათ"</item>
|
<item quantity="one">"არასწორი PIN. თქვენ %1$d შანსი დაგრჩათ"</item>
|
||||||
<item quantity="other">"არასწორი PIN. თქვენ %1$d შანსი დაგრჩათ"</item>
|
<item quantity="other">"არასწორი PIN. თქვენ %1$d შანსი დაგრჩათ"</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="screen_app_lock_use_biometric_android">"გამოიყენეთ ბიომეტრია"</string>
|
||||||
|
<string name="screen_app_lock_use_pin_android">"გამოიყენეთ PIN"</string>
|
||||||
<string name="screen_signout_in_progress_dialog_content">"გასვლა…"</string>
|
<string name="screen_signout_in_progress_dialog_content">"გასვლა…"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_app_lock_biometric_authentication">"uwierzytelnienie biometryczne"</string>
|
||||||
|
<string name="screen_app_lock_biometric_unlock">"odblokowanie biometryczne"</string>
|
||||||
|
<string name="screen_app_lock_biometric_unlock_title_android">"Odblokuj za pomocą biometrii"</string>
|
||||||
|
<string name="screen_app_lock_forgot_pin">"Nie pamiętasz kodu PIN?"</string>
|
||||||
|
<string name="screen_app_lock_settings_change_pin">"Zmień kod PIN"</string>
|
||||||
|
<string name="screen_app_lock_settings_enable_biometric_unlock">"Zezwól na uwierzytelnienie biometryczne"</string>
|
||||||
|
<string name="screen_app_lock_settings_remove_pin">"Usuń PIN"</string>
|
||||||
|
<string name="screen_app_lock_settings_remove_pin_alert_message">"Czy na pewno chcesz usunąć PIN?"</string>
|
||||||
|
<string name="screen_app_lock_settings_remove_pin_alert_title">"Usunąć PIN?"</string>
|
||||||
|
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"Zezwól na %1$s"</string>
|
||||||
|
<string name="screen_app_lock_setup_biometric_unlock_skip">"Wolę korzystać z kodu PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Zaoszczędź sobie trochę czasu i korzystaj z %1$s do odblokowywania aplikacji"</string>
|
||||||
|
<string name="screen_app_lock_setup_choose_pin">"Wybierz PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_confirm_pin">"Potwierdź PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_context">"Zablokuj %1$s, aby zwiększyć bezpieczeństwo swoich czatów.
|
||||||
|
|
||||||
|
Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz tego PINU, zostaniesz wylogowany z aplikacji."</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Nie możesz wybrać tego PINU ze względów bezpieczeństwa"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Wybierz inny kod PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Wprowadź ten sam kod PIN dwa razy"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PINY nie pasują do siebie"</string>
|
||||||
|
<string name="screen_app_lock_signout_alert_message">"Aby kontynuować, zaloguj się ponownie i utwórz nowy kod PIN"</string>
|
||||||
|
<string name="screen_app_lock_signout_alert_title">"Trwa wylogowywanie"</string>
|
||||||
|
<plurals name="screen_app_lock_subtitle">
|
||||||
|
<item quantity="one">"Masz %1$d próbę, żeby odblokować"</item>
|
||||||
|
<item quantity="few">"Masz %1$d próby, żeby odblokować"</item>
|
||||||
|
<item quantity="many">"Masz %1$d prób, żeby odblokować"</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="screen_app_lock_subtitle_wrong_pin">
|
||||||
|
<item quantity="one">"Błędny PIN. Pozostała %1$d próba"</item>
|
||||||
|
<item quantity="few">"Błędny PIN. Pozostały %1$d próby"</item>
|
||||||
|
<item quantity="many">"Błędny PIN. Pozostało %1$d prób"</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="screen_app_lock_use_biometric_android">"Użyj biometrii"</string>
|
||||||
|
<string name="screen_app_lock_use_pin_android">"Użyj kodu PIN"</string>
|
||||||
|
<string name="screen_signout_in_progress_dialog_content">"Wylogowywanie…"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_app_lock_forgot_pin">"Esqueceu o PIN?"</string>
|
||||||
|
<string name="screen_app_lock_settings_change_pin">"Mudar código de PIN"</string>
|
||||||
|
<string name="screen_app_lock_settings_enable_biometric_unlock">"Permitir desbloqueio biométrico"</string>
|
||||||
|
<string name="screen_app_lock_settings_remove_pin">"Remover PIN"</string>
|
||||||
|
<string name="screen_app_lock_settings_remove_pin_alert_message">"Tem certeza de que quer remover o PIN?"</string>
|
||||||
|
<string name="screen_app_lock_settings_remove_pin_alert_title">"Remover PIN?"</string>
|
||||||
|
<string name="screen_app_lock_setup_choose_pin">"Escolher PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_confirm_pin">"Confirmar PIN"</string>
|
||||||
|
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"Os PINs não correspondem"</string>
|
||||||
|
<string name="screen_app_lock_signout_alert_title">"Você está sendo desconectado"</string>
|
||||||
|
<plurals name="screen_app_lock_subtitle">
|
||||||
|
<item quantity="one">"Você tem %1$d tentativa de debloqueio"</item>
|
||||||
|
<item quantity="other">"Você tem %1$d tentativas de debloqueio"</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="screen_app_lock_subtitle_wrong_pin">
|
||||||
|
<item quantity="one">"PIN incorreto. Você tem mais %1$d chance"</item>
|
||||||
|
<item quantity="other">"PIN incorreto. Você tem mais %1$d chances"</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
<string name="screen_login_subtitle">"Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."</string>
|
<string name="screen_login_subtitle">"Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."</string>
|
||||||
<string name="screen_login_title">"კეთილი იყოს თქვენი მობრძანება!"</string>
|
<string name="screen_login_title">"კეთილი იყოს თქვენი მობრძანება!"</string>
|
||||||
<string name="screen_login_title_with_homeserver">"შესვლა %1$s-ში"</string>
|
<string name="screen_login_title_with_homeserver">"შესვლა %1$s-ში"</string>
|
||||||
|
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"ხელახლა ცდა"</string>
|
||||||
<string name="screen_server_confirmation_change_server">"შეცვალეთ ანგარიშის მომწოდებელი"</string>
|
<string name="screen_server_confirmation_change_server">"შეცვალეთ ანგარიშის მომწოდებელი"</string>
|
||||||
<string name="screen_server_confirmation_message_login_element_dot_io">"კერძო სერვერი Element-ის თანამშრომლებისთვის."</string>
|
<string name="screen_server_confirmation_message_login_element_dot_io">"კერძო სერვერი Element-ის თანამშრომლებისთვის."</string>
|
||||||
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."</string>
|
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."</string>
|
||||||
|
|
|
||||||
43
features/login/impl/src/main/res/values-pl/translations.xml
Normal file
43
features/login/impl/src/main/res/values-pl/translations.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_account_provider_change">"Zmień dostawcę konta"</string>
|
||||||
|
<string name="screen_account_provider_form_hint">"Adres serwera domowego"</string>
|
||||||
|
<string name="screen_account_provider_form_notice">"Wprowadź wyszukiwane hasło lub adres domeny."</string>
|
||||||
|
<string name="screen_account_provider_form_subtitle">"Szukaj serwera firmowego, społeczności lub prywatnego."</string>
|
||||||
|
<string name="screen_account_provider_form_title">"Znajdź dostawcę konta"</string>
|
||||||
|
<string name="screen_account_provider_signin_subtitle">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string>
|
||||||
|
<string name="screen_account_provider_signin_title">"Zamierzasz się zalogować %s"</string>
|
||||||
|
<string name="screen_account_provider_signup_subtitle">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string>
|
||||||
|
<string name="screen_account_provider_signup_title">"Zamierzasz założyć konto na %s"</string>
|
||||||
|
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org jest ogromnym i darmowym serwerem na publicznej sieci Matrix zapewniający bezpieczną i zdecentralizowaną komunikację zarządzaną przez Fundację Matrix.org."</string>
|
||||||
|
<string name="screen_change_account_provider_other">"Inne"</string>
|
||||||
|
<string name="screen_change_account_provider_subtitle">"Użyj innego dostawcy konta, takiego jak własny serwer lub konta służbowego."</string>
|
||||||
|
<string name="screen_change_account_provider_title">"Zmień dostawcę konta"</string>
|
||||||
|
<string name="screen_change_server_error_invalid_homeserver">"Nie mogliśmy połączyć się z tym serwerem domowym. Sprawdź, czy adres URL serwera został wprowadzony poprawnie. Jeśli adres URL jest poprawny, skontaktuj się z administratorem serwera w celu uzyskania dalszej pomocy."</string>
|
||||||
|
<string name="screen_change_server_error_no_sliding_sync_message">"Ten serwer obecnie nie obsługuje technologii Sliding Sync."</string>
|
||||||
|
<string name="screen_change_server_form_header">"Adres URL serwera domowego"</string>
|
||||||
|
<string name="screen_change_server_form_notice">"Możesz połączyć się tylko z serwerem, który obsługuje technologię Sliding Sync. Administrator serwera domowego będzie musiał ją skonfigurować. %1$s"</string>
|
||||||
|
<string name="screen_change_server_subtitle">"Jaki jest adres Twojego serwera?"</string>
|
||||||
|
<string name="screen_change_server_title">"Wybierz swój serwer"</string>
|
||||||
|
<string name="screen_login_error_deactivated_account">"To konto zostało dezaktywowane."</string>
|
||||||
|
<string name="screen_login_error_invalid_credentials">"Nieprawidłowa nazwa użytkownika i/lub hasło"</string>
|
||||||
|
<string name="screen_login_error_invalid_user_id">"To nie jest prawidłowy identyfikator użytkownika. Oczekiwany format: \'@user:homeserver.org\'"</string>
|
||||||
|
<string name="screen_login_error_unsupported_authentication">"Wybrany serwer domowy nie obsługuje uwierzytelniania hasłem, ani OIDC. Skontaktuj się z jego administratorem lub wybierz inny serwer domowy."</string>
|
||||||
|
<string name="screen_login_form_header">"Wprowadź swoje dane"</string>
|
||||||
|
<string name="screen_login_subtitle">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string>
|
||||||
|
<string name="screen_login_title">"Witaj ponownie!"</string>
|
||||||
|
<string name="screen_login_title_with_homeserver">"Zaloguj się do %1$s"</string>
|
||||||
|
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Spróbuj ponownie"</string>
|
||||||
|
<string name="screen_server_confirmation_change_server">"Zmień dostawcę konta"</string>
|
||||||
|
<string name="screen_server_confirmation_message_login_element_dot_io">"Serwer prywatny dla pracowników Element."</string>
|
||||||
|
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."</string>
|
||||||
|
<string name="screen_server_confirmation_message_register">"Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."</string>
|
||||||
|
<string name="screen_server_confirmation_title_login">"Zamierzasz się zalogować do %1$s"</string>
|
||||||
|
<string name="screen_server_confirmation_title_register">"Zamierzasz utworzyć konto na %1$s"</string>
|
||||||
|
<string name="screen_waitlist_message">"Obecnie istnieje duże zapotrzebowanie na %1$s na %2$s. Wróć do aplikacji za kilka dni i spróbuj ponownie.
|
||||||
|
|
||||||
|
Dziękujemy za Twoją cierpliwość!"</string>
|
||||||
|
<string name="screen_waitlist_message_success">"Witamy w %1$s!"</string>
|
||||||
|
<string name="screen_waitlist_title">"Już prawie gotowe!"</string>
|
||||||
|
<string name="screen_waitlist_title_success">"Witamy!"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_account_provider_change">"Alterar provedor da conta"</string>
|
||||||
|
<string name="screen_account_provider_form_hint">"Endereço do servidor"</string>
|
||||||
|
<string name="screen_account_provider_form_notice">"Insira um termo de pesquisa ou um endereço de domínio."</string>
|
||||||
|
<string name="screen_account_provider_form_subtitle">"Procure uma empresa, comunidade ou servidor privado."</string>
|
||||||
|
<string name="screen_account_provider_form_title">"Encontre um provedor de contas"</string>
|
||||||
|
<string name="screen_account_provider_signin_subtitle">"Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails."</string>
|
||||||
|
<string name="screen_account_provider_signin_title">"Você está prestes a entrar em %s"</string>
|
||||||
|
<string name="screen_account_provider_signup_subtitle">"Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails."</string>
|
||||||
|
<string name="screen_account_provider_signup_title">"Você está prestes a criar uma conta em %s"</string>
|
||||||
|
<string name="screen_change_account_provider_matrix_org_subtitle">"O Matrix.org é um grande servidor gratuito na rede pública Matrix para comunicação segura e descentralizada, administrado pela Fundação Matrix.org."</string>
|
||||||
|
<string name="screen_change_account_provider_other">"Outro"</string>
|
||||||
|
<string name="screen_change_account_provider_subtitle">"Use um provedor de conta diferente, como seu próprio servidor privado ou uma conta corporativa."</string>
|
||||||
|
<string name="screen_change_account_provider_title">"Alterar provedor da conta"</string>
|
||||||
|
<string name="screen_change_server_error_invalid_homeserver">"Não conseguimos acessar esse servidor. Verifique se você inseriu a URL do servidor corretamente. Se a URL estiver correta, entre em contato com o administrador do servidor para obter mais ajuda."</string>
|
||||||
|
<string name="screen_change_server_error_no_sliding_sync_message">"Este servidor atualmente não oferece suporte à tecnologia sliding sync."</string>
|
||||||
|
<string name="screen_change_server_form_header">"URL do servidor"</string>
|
||||||
|
<string name="screen_change_server_form_notice">"Você só pode se conectar a um servidor existente que ofereça suporte à tecnologia sliding sync. O administrador do seu servidor precisará configurá-lo. %1$s"</string>
|
||||||
|
<string name="screen_change_server_subtitle">"Qual é o endereço do seu servidor?"</string>
|
||||||
|
<string name="screen_change_server_title">"Selecione seu servidor"</string>
|
||||||
|
<string name="screen_login_error_deactivated_account">"Essa conta foi desativada."</string>
|
||||||
|
<string name="screen_login_error_invalid_credentials">"Nome de usuário e/ou senha incorretos"</string>
|
||||||
|
<string name="screen_login_error_invalid_user_id">"Esse não é um identificador de usuário válido. Formato esperado: \'@usuário:servidor.org\'"</string>
|
||||||
|
<string name="screen_login_error_unsupported_authentication">"O servidor selecionado não suporta senha ou login no OIDC. Entre em contato com o administrador ou escolha outro servidor."</string>
|
||||||
|
<string name="screen_login_form_header">"Insira seus dados"</string>
|
||||||
|
<string name="screen_login_subtitle">"A Matrix é uma rede aberta para comunicação segura e descentralizada."</string>
|
||||||
|
<string name="screen_login_title">"Bem-vindo de volta!"</string>
|
||||||
|
<string name="screen_login_title_with_homeserver">"Iniciar sessão em %1$s"</string>
|
||||||
|
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Tente novamente"</string>
|
||||||
|
<string name="screen_server_confirmation_change_server">"Alterar provedor da conta"</string>
|
||||||
|
<string name="screen_server_confirmation_message_login_element_dot_io">"Um servidor privado para funcionários do Element."</string>
|
||||||
|
<string name="screen_server_confirmation_message_login_matrix_dot_org">"A Matrix é uma rede aberta para comunicação segura e descentralizada."</string>
|
||||||
|
<string name="screen_server_confirmation_message_register">"Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails."</string>
|
||||||
|
<string name="screen_server_confirmation_title_login">"Você está prestes a fazer login em %1$s"</string>
|
||||||
|
<string name="screen_server_confirmation_title_register">"Você está prestes a criar uma conta em %1$s"</string>
|
||||||
|
<string name="screen_waitlist_message">"Há uma grande demanda por %1$s sobre %2$s no momento. Volte ao aplicativo em alguns dias e tente novamente.
|
||||||
|
|
||||||
|
Obrigado pela sua paciência!"</string>
|
||||||
|
<string name="screen_waitlist_message_success">"Bem-vindo ao %1$s!"</string>
|
||||||
|
<string name="screen_waitlist_title">"Você está quase lá."</string>
|
||||||
|
<string name="screen_waitlist_title_success">"Você está dentro."</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -4,5 +4,15 @@
|
||||||
<string name="screen_signout_confirmation_dialog_submit">"გამოსვლა"</string>
|
<string name="screen_signout_confirmation_dialog_submit">"გამოსვლა"</string>
|
||||||
<string name="screen_signout_confirmation_dialog_title">"გამოსვლა"</string>
|
<string name="screen_signout_confirmation_dialog_title">"გამოსვლა"</string>
|
||||||
<string name="screen_signout_in_progress_dialog_content">"გასვლა…"</string>
|
<string name="screen_signout_in_progress_dialog_content">"გასვლა…"</string>
|
||||||
|
<string name="screen_signout_key_backup_disabled_subtitle">"თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, დაკარგავთ წვდომას თქვენს დაშიფრულ შეტყობინებებზე."</string>
|
||||||
|
<string name="screen_signout_key_backup_disabled_title">"თქვენ გამორთეთ სარეზერვო ასლი"</string>
|
||||||
|
<string name="screen_signout_key_backup_offline_subtitle">"თქვენი გასაღებების სარეზერვო ასლის შექმნა მიმდინარეობდა იმ დროს, როდესაც გამოხვედით. დაკავშირდით ისევ ისე, რომ სარეზერვო ასლი შეიქმნას ანგარიშიდან გამოსვლის გარეშე."</string>
|
||||||
|
<string name="screen_signout_key_backup_offline_title">"თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"</string>
|
||||||
|
<string name="screen_signout_key_backup_ongoing_subtitle">"გთხოვთ დაელოდეთ ამის დასრულებას სისტემიდან გამოსვლამდე."</string>
|
||||||
|
<string name="screen_signout_key_backup_ongoing_title">"თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"</string>
|
||||||
<string name="screen_signout_preference_item">"გამოსვლა"</string>
|
<string name="screen_signout_preference_item">"გამოსვლა"</string>
|
||||||
|
<string name="screen_signout_recovery_disabled_subtitle">"თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, დაკარგავთ წვდომას თქვენს დაშიფრულ შეტყობინებებზე."</string>
|
||||||
|
<string name="screen_signout_recovery_disabled_title">"აღდგენა არ არის დაყენებული"</string>
|
||||||
|
<string name="screen_signout_save_recovery_key_subtitle">"თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, შესაძლოა დაკარგოთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე."</string>
|
||||||
|
<string name="screen_signout_save_recovery_key_title">"შეინახეთ თქვენი აღდგენის გასაღები?"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
18
features/logout/impl/src/main/res/values-pl/translations.xml
Normal file
18
features/logout/impl/src/main/res/values-pl/translations.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_signout_confirmation_dialog_content">"Czy na pewno chcesz się wylogować?"</string>
|
||||||
|
<string name="screen_signout_confirmation_dialog_submit">"Wyloguj się"</string>
|
||||||
|
<string name="screen_signout_confirmation_dialog_title">"Wyloguj się"</string>
|
||||||
|
<string name="screen_signout_in_progress_dialog_content">"Wylogowywanie…"</string>
|
||||||
|
<string name="screen_signout_key_backup_disabled_subtitle">"Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."</string>
|
||||||
|
<string name="screen_signout_key_backup_disabled_title">"Wyłączyłeś backup"</string>
|
||||||
|
<string name="screen_signout_key_backup_offline_subtitle">"Twoje klucze były nadal archiwizowane po przejściu w tryb offline. Połącz się ponownie, aby zapisać w chmurze przed wylogowaniem."</string>
|
||||||
|
<string name="screen_signout_key_backup_offline_title">"Twoje klucze są nadal archiwizowane"</string>
|
||||||
|
<string name="screen_signout_key_backup_ongoing_subtitle">"Zanim się wylogujesz, poczekaj na zakończenie operacji."</string>
|
||||||
|
<string name="screen_signout_key_backup_ongoing_title">"Twoje klucze są nadal archiwizowane"</string>
|
||||||
|
<string name="screen_signout_preference_item">"Wyloguj się"</string>
|
||||||
|
<string name="screen_signout_recovery_disabled_subtitle">"Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."</string>
|
||||||
|
<string name="screen_signout_recovery_disabled_title">"Nie ustawiono przywracania"</string>
|
||||||
|
<string name="screen_signout_save_recovery_key_subtitle">"Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."</string>
|
||||||
|
<string name="screen_signout_save_recovery_key_title">"Czy zapisałeś swój klucz przywracania?"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_signout_confirmation_dialog_content">"Você tem certeza que deseja sair?"</string>
|
||||||
|
<string name="screen_signout_confirmation_dialog_submit">"Sair"</string>
|
||||||
|
<string name="screen_signout_confirmation_dialog_title">"Sair"</string>
|
||||||
|
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
|
||||||
|
<string name="screen_signout_preference_item">"Sair"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -60,6 +60,7 @@ import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
||||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType
|
import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType
|
||||||
import io.element.android.libraries.matrix.api.room.isDm
|
import io.element.android.libraries.matrix.api.room.isDm
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.TimelineException
|
||||||
import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache
|
import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache
|
||||||
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
|
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
|
||||||
import io.element.android.libraries.matrix.ui.messages.reply.map
|
import io.element.android.libraries.matrix.ui.messages.reply.map
|
||||||
|
|
@ -436,7 +437,14 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
val eventId = capturedMode.eventId
|
val eventId = capturedMode.eventId
|
||||||
val transactionId = capturedMode.transactionId
|
val transactionId = capturedMode.transactionId
|
||||||
timelineController.invokeOnCurrentTimeline {
|
timelineController.invokeOnCurrentTimeline {
|
||||||
|
// First try to edit the message in the current timeline
|
||||||
editMessage(eventId, transactionId, message.markdown, message.html, message.mentions)
|
editMessage(eventId, transactionId, message.markdown, message.html, message.mentions)
|
||||||
|
.onFailure { cause ->
|
||||||
|
if (cause is TimelineException.EventNotFound && eventId != null) {
|
||||||
|
// if the event is not found in the timeline, try to edit the message directly
|
||||||
|
room.editMessage(eventId, message.markdown, message.html, message.mentions)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class ReactionSummaryPresenter @Inject constructor(
|
||||||
fun handleEvents(event: ReactionSummaryEvents) {
|
fun handleEvents(event: ReactionSummaryEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary(
|
is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary(
|
||||||
reactions = event.reactions,
|
reactions = event.reactions.toImmutableList(),
|
||||||
selectedKey = event.selectedKey,
|
selectedKey = event.selectedKey,
|
||||||
selectedEventId = event.eventId
|
selectedEventId = event.eventId
|
||||||
)
|
)
|
||||||
|
|
@ -73,8 +73,8 @@ class ReactionSummaryPresenter @Inject constructor(
|
||||||
avatarUrl = member?.avatarUrl
|
avatarUrl = member?.avatarUrl
|
||||||
)
|
)
|
||||||
sender.copy(user = user)
|
sender.copy(user = user)
|
||||||
})
|
}.toImmutableList())
|
||||||
})
|
}.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,14 @@ package io.element.android.features.messages.impl.timeline.components.reactionsu
|
||||||
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ReactionSummaryState(
|
data class ReactionSummaryState(
|
||||||
val target: Summary?,
|
val target: Summary?,
|
||||||
val eventSink: (ReactionSummaryEvents) -> Unit
|
val eventSink: (ReactionSummaryEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
data class Summary(
|
data class Summary(
|
||||||
val reactions: List<AggregatedReaction>,
|
val reactions: ImmutableList<AggregatedReaction>,
|
||||||
val selectedKey: String,
|
val selectedKey: String,
|
||||||
val selectedEventId: EventId
|
val selectedEventId: EventId
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -264,27 +264,27 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CharSequence.withFixedURLSpans(): CharSequence {
|
private fun CharSequence.withFixedURLSpans(): CharSequence {
|
||||||
if (this !is Spannable) return this
|
val spannable = this.toSpannable()
|
||||||
// Get all URL spans, as they will be removed by LinkifyCompat.addLinks
|
// Get all URL spans, as they will be removed by LinkifyCompat.addLinks
|
||||||
val oldURLSpans = getSpans<URLSpan>(0, length).associateWith {
|
val oldURLSpans = spannable.getSpans<URLSpan>(0, length).associateWith {
|
||||||
val start = getSpanStart(it)
|
val start = spannable.getSpanStart(it)
|
||||||
val end = getSpanEnd(it)
|
val end = spannable.getSpanEnd(it)
|
||||||
Pair(start, end)
|
Pair(start, end)
|
||||||
}
|
}
|
||||||
// Find and set as URLSpans any links present in the text
|
// Find and set as URLSpans any links present in the text
|
||||||
LinkifyCompat.addLinks(this, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES)
|
LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES)
|
||||||
// Restore old spans, remove new ones if there is a conflict
|
// Restore old spans, remove new ones if there is a conflict
|
||||||
for ((urlSpan, location) in oldURLSpans) {
|
for ((urlSpan, location) in oldURLSpans) {
|
||||||
val (start, end) = location
|
val (start, end) = location
|
||||||
val addedSpans = getSpans<URLSpan>(start, end).orEmpty()
|
val addedSpans = spannable.getSpans<URLSpan>(start, end).orEmpty()
|
||||||
if (addedSpans.isNotEmpty()) {
|
if (addedSpans.isNotEmpty()) {
|
||||||
for (span in addedSpans) {
|
for (span in addedSpans) {
|
||||||
removeSpan(span)
|
spannable.removeSpan(span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
spannable.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
}
|
}
|
||||||
return this
|
return spannable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,7 @@ class TimelineItemEventFactory @Inject constructor(
|
||||||
sentTime = timeFormatter.format(date),
|
sentTime = timeFormatter.format(date),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.toImmutableList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Sort aggregated reactions by count and then timestamp ascending, using
|
// Sort aggregated reactions by count and then timestamp ascending, using
|
||||||
|
|
@ -127,7 +128,9 @@ class TimelineItemEventFactory @Inject constructor(
|
||||||
compareByDescending<AggregatedReaction> { it.count }
|
compareByDescending<AggregatedReaction> { it.count }
|
||||||
.thenBy { it.senders[0].timestamp }
|
.thenBy { it.senders[0].timestamp }
|
||||||
)
|
)
|
||||||
return TimelineItemReactions(aggregatedReactions.toImmutableList())
|
return TimelineItemReactions(
|
||||||
|
reactions = aggregatedReactions.toImmutableList()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MatrixTimelineItem.Event.computeReadReceiptState(
|
private fun MatrixTimelineItem.Event.computeReadReceiptState(
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.model
|
||||||
|
|
||||||
import io.element.android.libraries.core.extensions.ellipsize
|
import io.element.android.libraries.core.extensions.ellipsize
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Length at which we ellipsize a reaction key for display
|
* Length at which we ellipsize a reaction key for display
|
||||||
|
|
@ -35,28 +36,22 @@ private const val MAX_DISPLAY_CHARS = 16
|
||||||
data class AggregatedReaction(
|
data class AggregatedReaction(
|
||||||
val currentUserId: UserId,
|
val currentUserId: UserId,
|
||||||
val key: String,
|
val key: String,
|
||||||
val senders: List<AggregatedReactionSender>
|
val senders: ImmutableList<AggregatedReactionSender>
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* The key to be displayed on screen.
|
* The key to be displayed on screen.
|
||||||
*
|
*
|
||||||
* See [MAX_DISPLAY_CHARS].
|
* See [MAX_DISPLAY_CHARS].
|
||||||
*/
|
*/
|
||||||
val displayKey: String by lazy {
|
val displayKey: String = key.ellipsize(MAX_DISPLAY_CHARS)
|
||||||
key.ellipsize(MAX_DISPLAY_CHARS)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of users who reacted with this key.
|
* The number of users who reacted with this key.
|
||||||
*/
|
*/
|
||||||
val count: Int by lazy {
|
val count: Int = senders.count()
|
||||||
senders.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the reaction has (also) been sent by the current user.
|
* True if the reaction has (also) been sent by the current user.
|
||||||
*/
|
*/
|
||||||
val isHighlighted: Boolean by lazy {
|
val isHighlighted: Boolean = senders.any { it.senderId == currentUserId }
|
||||||
senders.any { it.senderId.value == currentUserId.value }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.model
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
|
@ -53,6 +54,6 @@ fun anAggregatedReaction(
|
||||||
return AggregatedReaction(
|
return AggregatedReaction(
|
||||||
currentUserId = userId,
|
currentUserId = userId,
|
||||||
key = key,
|
key = key,
|
||||||
senders = senders
|
senders = senders.toImmutableList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,12 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.model
|
package io.element.android.features.messages.impl.timeline.model
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
@Immutable
|
||||||
data class AggregatedReactionSender(
|
data class AggregatedReactionSender(
|
||||||
val senderId: UserId,
|
val senderId: UserId,
|
||||||
val timestamp: Date,
|
val timestamp: Date,
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@
|
||||||
<string name="screen_room_attachment_source_poll">"გამოკითხვა"</string>
|
<string name="screen_room_attachment_source_poll">"გამოკითხვა"</string>
|
||||||
<string name="screen_room_attachment_text_formatting">"ტექსტის ფორმატირება"</string>
|
<string name="screen_room_attachment_text_formatting">"ტექსტის ფორმატირება"</string>
|
||||||
<string name="screen_room_encrypted_history_banner">"შეტყობინებების ისტორია ამჟამად მიუწვდომელია."</string>
|
<string name="screen_room_encrypted_history_banner">"შეტყობინებების ისტორია ამჟამად მიუწვდომელია."</string>
|
||||||
|
<string name="screen_room_encrypted_history_banner_unverified">"შეტყობინებების ისტორია ამ ოთახში მიუწვდომელია. დაადასტურეთ ეს მოწყობილობა თქვენი შეტყობინებების ისტორიის სანახავად."</string>
|
||||||
<string name="screen_room_invite_again_alert_message">"გსურთ მათი კვლავ მოწვევა?"</string>
|
<string name="screen_room_invite_again_alert_message">"გსურთ მათი კვლავ მოწვევა?"</string>
|
||||||
<string name="screen_room_invite_again_alert_title">"თქვენ მარტო ხართ ამ ჩატში"</string>
|
<string name="screen_room_invite_again_alert_title">"თქვენ მარტო ხართ ამ ჩატში"</string>
|
||||||
|
<string name="screen_room_mentions_at_room_subtitle">"მთელი ოთახისათვის შეტყობინება"</string>
|
||||||
<string name="screen_room_mentions_at_room_title">"ყველა"</string>
|
<string name="screen_room_mentions_at_room_title">"ყველა"</string>
|
||||||
<string name="screen_room_retry_send_menu_send_again_action">"Ხელახლა გაგზავნა"</string>
|
<string name="screen_room_retry_send_menu_send_again_action">"Ხელახლა გაგზავნა"</string>
|
||||||
<string name="screen_room_retry_send_menu_title">"თქვენი შეტყობინების გაგზავნა ვერ მოხერხდა"</string>
|
<string name="screen_room_retry_send_menu_title">"თქვენი შეტყობინების გაგზავნა ვერ მოხერხდა"</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="emoji_picker_category_activity">"Aktywności"</string>
|
||||||
|
<string name="emoji_picker_category_flags">"Flagi"</string>
|
||||||
|
<string name="emoji_picker_category_foods">"Jedzenie i napoje"</string>
|
||||||
|
<string name="emoji_picker_category_nature">"Zwierzęta i natura"</string>
|
||||||
|
<string name="emoji_picker_category_objects">"Obiekty"</string>
|
||||||
|
<string name="emoji_picker_category_people">"Buźki i osoby"</string>
|
||||||
|
<string name="emoji_picker_category_places">"Podróż i miejsca"</string>
|
||||||
|
<string name="emoji_picker_category_symbols">"Symbole"</string>
|
||||||
|
<string name="screen_report_content_block_user">"Zablokuj użytkownika"</string>
|
||||||
|
<string name="screen_report_content_block_user_hint">"Sprawdź, czy chcesz ukryć wszystkie bieżące i przyszłe wiadomości od tego użytkownika."</string>
|
||||||
|
<string name="screen_report_content_explanation">"Ta wiadomość zostanie zgłoszona do administratora Twojego serwera domowego. Nie będzie mógł on przeczytać żadnych zaszyfrowanych wiadomości."</string>
|
||||||
|
<string name="screen_report_content_hint">"Powód zgłoszenia treści"</string>
|
||||||
|
<string name="screen_room_attachment_source_camera">"Kamera"</string>
|
||||||
|
<string name="screen_room_attachment_source_camera_photo">"Zrób zdjęcie"</string>
|
||||||
|
<string name="screen_room_attachment_source_camera_video">"Nagraj film"</string>
|
||||||
|
<string name="screen_room_attachment_source_files">"Załącznik"</string>
|
||||||
|
<string name="screen_room_attachment_source_gallery">"Zdjęcia i filmy"</string>
|
||||||
|
<string name="screen_room_attachment_source_location">"Lokalizacja"</string>
|
||||||
|
<string name="screen_room_attachment_source_poll">"Ankieta"</string>
|
||||||
|
<string name="screen_room_attachment_text_formatting">"Formatowanie tekstu"</string>
|
||||||
|
<string name="screen_room_encrypted_history_banner">"Historia wiadomości jest obecnie niedostępna."</string>
|
||||||
|
<string name="screen_room_encrypted_history_banner_unverified">"Historia wiadomości jest niedostępna w tym pokoju. Zweryfikuj to urządzenie, aby zobaczyć historię wiadomości."</string>
|
||||||
|
<string name="screen_room_invite_again_alert_message">"Czy chcesz zaprosić ich z powrotem?"</string>
|
||||||
|
<string name="screen_room_invite_again_alert_title">"Jesteś sam na tym czacie"</string>
|
||||||
|
<string name="screen_room_mentions_at_room_subtitle">"Powiadom cały pokój"</string>
|
||||||
|
<string name="screen_room_mentions_at_room_title">"Wszyscy"</string>
|
||||||
|
<string name="screen_room_retry_send_menu_send_again_action">"Wyślij ponownie"</string>
|
||||||
|
<string name="screen_room_retry_send_menu_title">"Nie udało się wysłać wiadomości"</string>
|
||||||
|
<string name="screen_room_timeline_add_reaction">"Dodaj emoji"</string>
|
||||||
|
<string name="screen_room_timeline_beginning_of_room">"To jest początek %1$s"</string>
|
||||||
|
<string name="screen_room_timeline_beginning_of_room_no_name">"To jest początek tej konwersacji"</string>
|
||||||
|
<string name="screen_room_timeline_less_reactions">"Pokaż mniej"</string>
|
||||||
|
<string name="screen_room_timeline_message_copied">"Skopiowano wiadomość"</string>
|
||||||
|
<string name="screen_room_timeline_no_permission_to_post">"Nie masz uprawnień, aby pisać w tym pokoju"</string>
|
||||||
|
<string name="screen_room_timeline_reactions_show_less">"Pokaż mniej"</string>
|
||||||
|
<string name="screen_room_timeline_reactions_show_more">"Pokaż więcej"</string>
|
||||||
|
<string name="screen_room_timeline_read_marker_title">"Nowe"</string>
|
||||||
|
<plurals name="screen_room_timeline_state_changes">
|
||||||
|
<item quantity="one">"%1$d zmiana pokoju"</item>
|
||||||
|
<item quantity="few">"%1$d zmian pokoju"</item>
|
||||||
|
<item quantity="many">"%1$d zmiany pokoju"</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="screen_room_typing_notification">
|
||||||
|
<item quantity="one">"%1$s piszę"</item>
|
||||||
|
<item quantity="few">"%1$s piszą"</item>
|
||||||
|
<item quantity="many">"%1$s piszą"</item>
|
||||||
|
</plurals>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="emoji_picker_category_activity">"Atividades"</string>
|
||||||
|
<string name="emoji_picker_category_flags">"Bandeiras"</string>
|
||||||
|
<string name="emoji_picker_category_foods">"Comida & Bebida"</string>
|
||||||
|
<string name="emoji_picker_category_nature">"Animais & Natureza"</string>
|
||||||
|
<string name="emoji_picker_category_objects">"Objetos"</string>
|
||||||
|
<string name="emoji_picker_category_people">"Sorrisos & Pessoas"</string>
|
||||||
|
<string name="emoji_picker_category_places">"Viagens & Lugares"</string>
|
||||||
|
<string name="emoji_picker_category_symbols">"Símbolos"</string>
|
||||||
|
<string name="screen_report_content_block_user">"Bloquear usuário"</string>
|
||||||
|
<string name="screen_report_content_block_user_hint">"Marque se você deseja ocultar todas as mensagens atuais e futuras desse usuário"</string>
|
||||||
|
<string name="screen_report_content_explanation">"Essa mensagem será reportada ao administrador do seu homeserver. Eles não conseguirão ler nenhuma mensagem criptografada."</string>
|
||||||
|
<string name="screen_report_content_hint">"Motivo para denunciar este conteúdo"</string>
|
||||||
|
<string name="screen_room_attachment_source_camera">"Câmera"</string>
|
||||||
|
<string name="screen_room_attachment_source_camera_photo">"Tirar foto"</string>
|
||||||
|
<string name="screen_room_attachment_source_camera_video">"Gravar vídeo"</string>
|
||||||
|
<string name="screen_room_attachment_source_files">"Anexo"</string>
|
||||||
|
<string name="screen_room_attachment_source_gallery">"Biblioteca de fotos e vídeos"</string>
|
||||||
|
<string name="screen_room_attachment_source_location">"Localização"</string>
|
||||||
|
<string name="screen_room_attachment_source_poll">"Enquete"</string>
|
||||||
|
<string name="screen_room_attachment_text_formatting">"Formatação de texto"</string>
|
||||||
|
<string name="screen_room_encrypted_history_banner">"O histórico de mensagens não está disponível no momento."</string>
|
||||||
|
<string name="screen_room_invite_again_alert_message">"Gostaria de convidá-los de volta?"</string>
|
||||||
|
<string name="screen_room_invite_again_alert_title">"Você está sozinho neste chat"</string>
|
||||||
|
<string name="screen_room_mentions_at_room_title">"Todos"</string>
|
||||||
|
<string name="screen_room_retry_send_menu_send_again_action">"Enviar novamente"</string>
|
||||||
|
<string name="screen_room_retry_send_menu_title">"Sua mensagem não foi enviada"</string>
|
||||||
|
<string name="screen_room_timeline_add_reaction">"Adicionar emoji"</string>
|
||||||
|
<string name="screen_room_timeline_beginning_of_room">"Este é o início do %1$s."</string>
|
||||||
|
<string name="screen_room_timeline_beginning_of_room_no_name">"Este é o início desta conversa."</string>
|
||||||
|
<string name="screen_room_timeline_less_reactions">"Mostrar menos"</string>
|
||||||
|
<string name="screen_room_timeline_message_copied">"Mensagem copiada"</string>
|
||||||
|
<string name="screen_room_timeline_no_permission_to_post">"Você não tem permissão para postar nesta sala"</string>
|
||||||
|
<string name="screen_room_timeline_reactions_show_less">"Mostrar menos"</string>
|
||||||
|
<string name="screen_room_timeline_reactions_show_more">"Mostrar mais"</string>
|
||||||
|
<string name="screen_room_timeline_read_marker_title">"Novo"</string>
|
||||||
|
<plurals name="screen_room_timeline_state_changes">
|
||||||
|
<item quantity="one">"%1$d mudança de sala"</item>
|
||||||
|
<item quantity="other">"%1$d mudanças de salas"</item>
|
||||||
|
</plurals>
|
||||||
|
</resources>
|
||||||
|
|
@ -65,6 +65,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
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.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
|
@ -103,6 +104,7 @@ import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilTimeout
|
import io.element.android.tests.testutils.consumeItemsUntilTimeout
|
||||||
import io.element.android.tests.testutils.lambda.assert
|
import io.element.android.tests.testutils.lambda.assert
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.lambda.value
|
import io.element.android.tests.testutils.lambda.value
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
|
|
@ -136,7 +138,7 @@ class MessagesPresenterTest {
|
||||||
assertThat(initialState.roomAvatar)
|
assertThat(initialState.roomAvatar)
|
||||||
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
|
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
|
||||||
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
|
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
|
||||||
assertThat(initialState.userHasPermissionToRedactOwn).isFalse()
|
assertThat(initialState.userHasPermissionToRedactOwn).isTrue()
|
||||||
assertThat(initialState.hasNetworkConnection).isTrue()
|
assertThat(initialState.hasNetworkConnection).isTrue()
|
||||||
assertThat(initialState.snackbarMessage).isNull()
|
assertThat(initialState.snackbarMessage).isNull()
|
||||||
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
|
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
|
||||||
|
|
@ -147,7 +149,13 @@ class MessagesPresenterTest {
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Test
|
@Test
|
||||||
fun `present - check that the room's unread flag is removed`() = runTest {
|
fun `present - check that the room's unread flag is removed`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
assertThat(room.markAsReadCalls).isEmpty()
|
assertThat(room.markAsReadCalls).isEmpty()
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -161,8 +169,13 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
|
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanUserJoinCall(Result.success(false))
|
canUserJoinCallResult = { Result.success(false) },
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(hasRoomCall = true))
|
givenRoomInfo(aRoomInfo(hasRoomCall = true))
|
||||||
}
|
}
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
|
|
@ -183,7 +196,14 @@ class MessagesPresenterTest {
|
||||||
val timeline = FakeTimeline().apply {
|
val timeline = FakeTimeline().apply {
|
||||||
this.toggleReactionLambda = toggleReactionSuccess
|
this.toggleReactionLambda = toggleReactionSuccess
|
||||||
}
|
}
|
||||||
val room = FakeMatrixRoom(liveTimeline = timeline)
|
val room = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -213,7 +233,14 @@ class MessagesPresenterTest {
|
||||||
val timeline = FakeTimeline().apply {
|
val timeline = FakeTimeline().apply {
|
||||||
this.toggleReactionLambda = toggleReactionSuccess
|
this.toggleReactionLambda = toggleReactionSuccess
|
||||||
}
|
}
|
||||||
val room = FakeMatrixRoom(liveTimeline = timeline)
|
val room = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -266,6 +293,11 @@ class MessagesPresenterTest {
|
||||||
val event = aMessageEvent()
|
val event = aMessageEvent()
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
eventPermalinkResult = { Result.success("a link") },
|
eventPermalinkResult = { Result.success("a link") },
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
)
|
)
|
||||||
val presenter = createMessagesPresenter(
|
val presenter = createMessagesPresenter(
|
||||||
clipboardHelper = clipboardHelper,
|
clipboardHelper = clipboardHelper,
|
||||||
|
|
@ -448,7 +480,14 @@ class MessagesPresenterTest {
|
||||||
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||||
|
|
||||||
val liveTimeline = FakeTimeline()
|
val liveTimeline = FakeTimeline()
|
||||||
val matrixRoom = FakeMatrixRoom(liveTimeline = liveTimeline)
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
liveTimeline = liveTimeline,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
|
|
||||||
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
|
val redactEventLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String? -> Result.success(true) }
|
||||||
liveTimeline.redactEventLambda = redactEventLambda
|
liveTimeline.redactEventLambda = redactEventLambda
|
||||||
|
|
@ -513,7 +552,16 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - shows prompt to reinvite users in DM`() = runTest {
|
fun `present - shows prompt to reinvite users in DM`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 1L)
|
val room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
isDirect = true,
|
||||||
|
activeMemberCount = 1L,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -539,7 +587,16 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
|
fun `present - doesn't show reinvite prompt in non-direct room`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = false, activeMemberCount = 1L)
|
val room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
isDirect = false,
|
||||||
|
activeMemberCount = 1L,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -554,7 +611,16 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - doesn't show reinvite prompt if other party is present`() = runTest {
|
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 room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
isDirect = true,
|
||||||
|
activeMemberCount = 2L,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -569,7 +635,16 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle reinviting other user when memberlist is ready`() = runTest {
|
fun `present - handle reinviting other user when memberlist is ready`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_SESSION_ID)
|
val inviteUserResult = lambdaRecorder { _: UserId -> Result.success(Unit) }
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
inviteUserResult = inviteUserResult,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
room.givenRoomMembersState(
|
room.givenRoomMembersState(
|
||||||
MatrixRoomMembersState.Ready(
|
MatrixRoomMembersState.Ready(
|
||||||
persistentListOf(
|
persistentListOf(
|
||||||
|
|
@ -589,13 +664,22 @@ class MessagesPresenterTest {
|
||||||
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
||||||
val newState = awaitItem()
|
val newState = awaitItem()
|
||||||
assertThat(newState.inviteProgress.isSuccess()).isTrue()
|
assertThat(newState.inviteProgress.isSuccess()).isTrue()
|
||||||
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2)
|
inviteUserResult.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle reinviting other user when memberlist is error`() = runTest {
|
fun `present - handle reinviting other user when memberlist is error`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_SESSION_ID)
|
val inviteUserResult = lambdaRecorder { _: UserId -> Result.success(Unit) }
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
inviteUserResult = inviteUserResult,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
room.givenRoomMembersState(
|
room.givenRoomMembersState(
|
||||||
MatrixRoomMembersState.Error(
|
MatrixRoomMembersState.Error(
|
||||||
failure = Throwable(),
|
failure = Throwable(),
|
||||||
|
|
@ -618,13 +702,20 @@ class MessagesPresenterTest {
|
||||||
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
||||||
val newState = awaitItem()
|
val newState = awaitItem()
|
||||||
assertThat(newState.inviteProgress.isSuccess()).isTrue()
|
assertThat(newState.inviteProgress.isSuccess()).isTrue()
|
||||||
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2)
|
inviteUserResult.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle reinviting other user when memberlist is not ready`() = runTest {
|
fun `present - handle reinviting other user when memberlist is not ready`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_SESSION_ID)
|
val room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
|
room.givenRoomMembersState(MatrixRoomMembersState.Unknown)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -642,7 +733,15 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle reinviting other user when inviting fails`() = runTest {
|
fun `present - handle reinviting other user when inviting fails`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_SESSION_ID)
|
val room = FakeMatrixRoom(
|
||||||
|
sessionId = A_SESSION_ID,
|
||||||
|
inviteUserResult = { Result.failure(Throwable("Oops!")) },
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
room.givenRoomMembersState(
|
room.givenRoomMembersState(
|
||||||
MatrixRoomMembersState.Ready(
|
MatrixRoomMembersState.Ready(
|
||||||
persistentListOf(
|
persistentListOf(
|
||||||
|
|
@ -651,7 +750,6 @@ class MessagesPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
room.givenInviteUserResult(Result.failure(Throwable("Oops!")))
|
|
||||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -671,8 +769,19 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - permission to post`() = runTest {
|
fun `present - permission to post`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom()
|
val matrixRoom = FakeMatrixRoom(
|
||||||
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(true))
|
canUserSendMessageResult = { _, messageEventType ->
|
||||||
|
when (messageEventType) {
|
||||||
|
MessageEventType.ROOM_MESSAGE -> Result.success(true)
|
||||||
|
MessageEventType.REACTION -> Result.success(true)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -684,8 +793,19 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - no permission to post`() = runTest {
|
fun `present - no permission to post`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom()
|
val matrixRoom = FakeMatrixRoom(
|
||||||
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(false))
|
canUserSendMessageResult = { _, messageEventType ->
|
||||||
|
when (messageEventType) {
|
||||||
|
MessageEventType.ROOM_MESSAGE -> Result.success(false)
|
||||||
|
MessageEventType.REACTION -> Result.success(false)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -700,7 +820,13 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - permission to redact own`() = runTest {
|
fun `present - permission to redact own`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(canRedactOwn = true)
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(false) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -714,7 +840,13 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - permission to redact other`() = runTest {
|
fun `present - permission to redact other`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom(canRedactOther = true)
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(false) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -754,7 +886,13 @@ class MessagesPresenterTest {
|
||||||
|
|
||||||
private fun TestScope.createMessagesPresenter(
|
private fun TestScope.createMessagesPresenter(
|
||||||
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||||
matrixRoom: MatrixRoom = FakeMatrixRoom().apply {
|
matrixRoom: MatrixRoom = FakeMatrixRoom(
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
canRedactOwnResult = { Result.success(true) },
|
||||||
|
canRedactOtherResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(id = roomId, name = ""))
|
givenRoomInfo(aRoomInfo(id = roomId, name = ""))
|
||||||
},
|
},
|
||||||
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
|
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
|
||||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
|
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
|
||||||
import io.element.android.features.messages.impl.attachments.preview.SendActionState
|
import io.element.android.features.messages.impl.attachments.preview.SendActionState
|
||||||
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
|
|
@ -34,6 +36,7 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||||
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||||
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
|
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -49,13 +52,16 @@ class AttachmentsPreviewPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send media success scenario`() = runTest {
|
fun `present - send media success scenario`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> {
|
||||||
room.givenProgressCallbackValues(
|
Result.success(FakeMediaUploadHandler())
|
||||||
listOf(
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
progressCallbackValues = listOf(
|
||||||
Pair(0, 10),
|
Pair(0, 10),
|
||||||
Pair(5, 10),
|
Pair(5, 10),
|
||||||
Pair(10, 10)
|
Pair(10, 10)
|
||||||
)
|
),
|
||||||
|
sendMediaResult = sendMediaResult,
|
||||||
)
|
)
|
||||||
val presenter = createAttachmentsPreviewPresenter(room = room)
|
val presenter = createAttachmentsPreviewPresenter(room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -70,15 +76,19 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f))
|
||||||
val successState = awaitItem()
|
val successState = awaitItem()
|
||||||
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
|
||||||
assertThat(room.sendMediaCount).isEqualTo(1)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send media failure scenario`() = runTest {
|
fun `present - send media failure scenario`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
|
||||||
val failure = MediaPreProcessor.Failure(null)
|
val failure = MediaPreProcessor.Failure(null)
|
||||||
room.givenSendMediaResult(Result.failure(failure))
|
val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> {
|
||||||
|
Result.failure(failure)
|
||||||
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
sendMediaResult = sendMediaResult,
|
||||||
|
)
|
||||||
val presenter = createAttachmentsPreviewPresenter(room = room)
|
val presenter = createAttachmentsPreviewPresenter(room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -90,7 +100,7 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
val failureState = awaitItem()
|
val failureState = awaitItem()
|
||||||
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
|
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
|
||||||
assertThat(room.sendMediaCount).isEqualTo(0)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState)
|
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState)
|
||||||
val clearedState = awaitItem()
|
val clearedState = awaitItem()
|
||||||
assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,14 @@ import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
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_ID
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -81,7 +84,12 @@ class ReportMessagePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `presenter - handle successful report and block user`() = runTest {
|
fun `presenter - handle successful report and block user`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
reportContentResult = reportContentResult
|
||||||
|
)
|
||||||
val presenter = createReportMessagePresenter(matrixRoom = room)
|
val presenter = createReportMessagePresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -92,13 +100,18 @@ class ReportMessagePresenterTest {
|
||||||
initialState.eventSink(ReportMessageEvents.Report)
|
initialState.eventSink(ReportMessageEvents.Report)
|
||||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
||||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
reportContentResult.assertions().isCalledOnce()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `presenter - handle successful report`() = runTest {
|
fun `presenter - handle successful report`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
reportContentResult = reportContentResult
|
||||||
|
)
|
||||||
val presenter = createReportMessagePresenter(matrixRoom = room)
|
val presenter = createReportMessagePresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -107,15 +120,18 @@ class ReportMessagePresenterTest {
|
||||||
initialState.eventSink(ReportMessageEvents.Report)
|
initialState.eventSink(ReportMessageEvents.Report)
|
||||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
||||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
reportContentResult.assertions().isCalledOnce()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `presenter - handle failed report`() = runTest {
|
fun `presenter - handle failed report`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
|
||||||
givenReportContentResult(Result.failure(Exception("Failed to report content")))
|
Result.failure(Exception("Failed to report content"))
|
||||||
}
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
reportContentResult = reportContentResult
|
||||||
|
)
|
||||||
val presenter = createReportMessagePresenter(matrixRoom = room)
|
val presenter = createReportMessagePresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -125,7 +141,7 @@ class ReportMessagePresenterTest {
|
||||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
val resultState = awaitItem()
|
val resultState = awaitItem()
|
||||||
assertThat(resultState.result).isInstanceOf(AsyncAction.Failure::class.java)
|
assertThat(resultState.result).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
reportContentResult.assertions().isCalledOnce()
|
||||||
|
|
||||||
resultState.eventSink(ReportMessageEvents.ClearError)
|
resultState.eventSink(ReportMessageEvents.ClearError)
|
||||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
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
|
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.TransactionId
|
||||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||||
|
|
@ -55,6 +56,7 @@ import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
||||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType
|
import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.TimelineException
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||||
import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE
|
import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
|
@ -67,6 +69,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_4
|
import io.element.android.libraries.matrix.test.A_USER_ID_4
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
|
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
||||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
|
|
@ -297,7 +300,13 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send message with rich text enabled`() = runTest {
|
fun `present - send message with rich text enabled`() = runTest {
|
||||||
val presenter = createPresenter(this)
|
val presenter = createPresenter(
|
||||||
|
coroutineScope = this,
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
sendMessageResult = { _, _, _ -> Result.success(Unit) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
remember(state, state.textEditorState.messageHtml()) { state }
|
remember(state, state.textEditorState.messageHtml()) { state }
|
||||||
|
|
@ -324,7 +333,14 @@ class MessageComposerPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - send message with plain text enabled`() = runTest {
|
fun `present - send message with plain text enabled`() = runTest {
|
||||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("") })
|
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("") })
|
||||||
val presenter = createPresenter(this, isRichTextEditorEnabled = false)
|
val presenter = createPresenter(
|
||||||
|
coroutineScope = this,
|
||||||
|
isRichTextEditorEnabled = false,
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
sendMessageResult = { _, _, _ -> Result.success(Unit) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
val messageMarkdown = state.textEditorState.messageMarkdown(permalinkBuilder)
|
val messageMarkdown = state.textEditorState.messageMarkdown(permalinkBuilder)
|
||||||
|
|
@ -358,7 +374,10 @@ class MessageComposerPresenterTest {
|
||||||
val timeline = FakeTimeline().apply {
|
val timeline = FakeTimeline().apply {
|
||||||
this.editMessageLambda = editMessageLambda
|
this.editMessageLambda = editMessageLambda
|
||||||
}
|
}
|
||||||
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline)
|
val fakeMatrixRoom = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
fakeMatrixRoom,
|
fakeMatrixRoom,
|
||||||
|
|
@ -399,6 +418,67 @@ class MessageComposerPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - edit sent message event not found`() = runTest {
|
||||||
|
val timelineEditMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List<Mention> ->
|
||||||
|
Result.failure<Unit>(TimelineException.EventNotFound)
|
||||||
|
}
|
||||||
|
val timeline = FakeTimeline().apply {
|
||||||
|
this.editMessageLambda = timelineEditMessageLambda
|
||||||
|
}
|
||||||
|
val roomEditMessageLambda = lambdaRecorder { _: EventId?, _: String, _: String?, _: List<Mention> ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val fakeMatrixRoom = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
).apply {
|
||||||
|
this.editMessageLambda = roomEditMessageLambda
|
||||||
|
}
|
||||||
|
val presenter = createPresenter(
|
||||||
|
this,
|
||||||
|
fakeMatrixRoom,
|
||||||
|
)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
val state = presenter.present()
|
||||||
|
remember(state, state.textEditorState.messageHtml()) { state }
|
||||||
|
}.test {
|
||||||
|
val initialState = awaitFirstItem()
|
||||||
|
assertThat(initialState.textEditorState.messageHtml()).isEqualTo("")
|
||||||
|
val mode = anEditMode()
|
||||||
|
initialState.eventSink.invoke(MessageComposerEvents.SetMode(mode))
|
||||||
|
val withMessageState = awaitItem()
|
||||||
|
assertThat(withMessageState.mode).isEqualTo(mode)
|
||||||
|
assertThat(withMessageState.textEditorState.messageHtml()).isEqualTo(A_MESSAGE)
|
||||||
|
withMessageState.textEditorState.setHtml(ANOTHER_MESSAGE)
|
||||||
|
val withEditedMessageState = awaitItem()
|
||||||
|
assertThat(withEditedMessageState.textEditorState.messageHtml()).isEqualTo(ANOTHER_MESSAGE)
|
||||||
|
withEditedMessageState.eventSink.invoke(MessageComposerEvents.SendMessage)
|
||||||
|
skipItems(1)
|
||||||
|
val messageSentState = awaitItem()
|
||||||
|
assertThat(messageSentState.textEditorState.messageHtml()).isEqualTo("")
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assert(timelineEditMessageLambda)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(AN_EVENT_ID), value(null), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any())
|
||||||
|
|
||||||
|
assert(roomEditMessageLambda)
|
||||||
|
.isCalledOnce()
|
||||||
|
.with(value(AN_EVENT_ID), value(ANOTHER_MESSAGE), value(ANOTHER_MESSAGE), any())
|
||||||
|
|
||||||
|
assertThat(analyticsService.capturedEvents).containsExactly(
|
||||||
|
Composer(
|
||||||
|
inThread = false,
|
||||||
|
isEditing = true,
|
||||||
|
isReply = false,
|
||||||
|
messageType = Composer.MessageType.Text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - edit not sent message`() = runTest {
|
fun `present - edit not sent message`() = runTest {
|
||||||
val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List<Mention> ->
|
val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List<Mention> ->
|
||||||
|
|
@ -407,7 +487,10 @@ class MessageComposerPresenterTest {
|
||||||
val timeline = FakeTimeline().apply {
|
val timeline = FakeTimeline().apply {
|
||||||
this.editMessageLambda = editMessageLambda
|
this.editMessageLambda = editMessageLambda
|
||||||
}
|
}
|
||||||
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline)
|
val fakeMatrixRoom = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
fakeMatrixRoom,
|
fakeMatrixRoom,
|
||||||
|
|
@ -456,7 +539,10 @@ class MessageComposerPresenterTest {
|
||||||
val timeline = FakeTimeline().apply {
|
val timeline = FakeTimeline().apply {
|
||||||
this.replyMessageLambda = replyMessageLambda
|
this.replyMessageLambda = replyMessageLambda
|
||||||
}
|
}
|
||||||
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline)
|
val fakeMatrixRoom = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
fakeMatrixRoom,
|
fakeMatrixRoom,
|
||||||
|
|
@ -524,7 +610,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Pick image from gallery`() = runTest {
|
fun `present - Pick image from gallery`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(this, room = room)
|
val presenter = createPresenter(this, room = room)
|
||||||
pickerProvider.givenMimeType(MimeTypes.Images)
|
pickerProvider.givenMimeType(MimeTypes.Images)
|
||||||
mediaPreProcessor.givenResult(
|
mediaPreProcessor.givenResult(
|
||||||
|
|
@ -557,7 +645,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Pick video from gallery`() = runTest {
|
fun `present - Pick video from gallery`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(this, room = room)
|
val presenter = createPresenter(this, room = room)
|
||||||
pickerProvider.givenMimeType(MimeTypes.Videos)
|
pickerProvider.givenMimeType(MimeTypes.Videos)
|
||||||
mediaPreProcessor.givenResult(
|
mediaPreProcessor.givenResult(
|
||||||
|
|
@ -607,13 +697,17 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Pick file from storage`() = runTest {
|
fun `present - Pick file from storage`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val sendMediaResult = lambdaRecorder { _: ProgressCallback? ->
|
||||||
room.givenProgressCallbackValues(
|
Result.success(FakeMediaUploadHandler())
|
||||||
listOf(
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
progressCallbackValues = listOf(
|
||||||
Pair(0, 10),
|
Pair(0, 10),
|
||||||
Pair(5, 10),
|
Pair(5, 10),
|
||||||
Pair(10, 10)
|
Pair(10, 10)
|
||||||
)
|
),
|
||||||
|
sendMediaResult = sendMediaResult,
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
)
|
)
|
||||||
val presenter = createPresenter(this, room = room)
|
val presenter = createPresenter(this, room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -629,13 +723,15 @@ class MessageComposerPresenterTest {
|
||||||
assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f))
|
assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f))
|
||||||
val sentState = awaitItem()
|
val sentState = awaitItem()
|
||||||
assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None)
|
assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None)
|
||||||
assertThat(room.sendMediaCount).isEqualTo(1)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - create poll`() = runTest {
|
fun `present - create poll`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(this, room = room)
|
val presenter = createPresenter(this, room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -652,7 +748,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - share location`() = runTest {
|
fun `present - share location`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(this, room = room)
|
val presenter = createPresenter(this, room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -669,7 +767,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Take photo`() = runTest {
|
fun `present - Take photo`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
|
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
|
|
@ -689,7 +789,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Take photo with permission request`() = runTest {
|
fun `present - Take photo with permission request`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val permissionPresenter = FakePermissionsPresenter()
|
val permissionPresenter = FakePermissionsPresenter()
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
|
|
@ -714,7 +816,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Record video`() = runTest {
|
fun `present - Record video`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
|
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
|
|
@ -734,7 +838,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Record video with permission request`() = runTest {
|
fun `present - Record video with permission request`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val permissionPresenter = FakePermissionsPresenter()
|
val permissionPresenter = FakePermissionsPresenter()
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
this,
|
this,
|
||||||
|
|
@ -759,9 +865,10 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Uploading media failure can be recovered from`() = runTest {
|
fun `present - Uploading media failure can be recovered from`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenSendMediaResult(Result.failure(Exception()))
|
sendMediaResult = { Result.failure(Exception()) },
|
||||||
}
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(this, room = room)
|
val presenter = createPresenter(this, room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -842,15 +949,17 @@ class MessageComposerPresenterTest {
|
||||||
val invitedUser = aRoomMember(userId = A_USER_ID_3, membership = RoomMembershipState.INVITE)
|
val invitedUser = aRoomMember(userId = A_USER_ID_3, membership = RoomMembershipState.INVITE)
|
||||||
val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN)
|
val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN)
|
||||||
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
||||||
|
var canUserTriggerRoomNotificationResult = true
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
|
canUserTriggerRoomNotificationResult = { Result.success(canUserTriggerRoomNotificationResult) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomMembersState(
|
givenRoomMembersState(
|
||||||
MatrixRoomMembersState.Ready(
|
MatrixRoomMembersState.Ready(
|
||||||
persistentListOf(currentUser, invitedUser, bob, david),
|
persistentListOf(currentUser, invitedUser, bob, david),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
givenCanTriggerRoomNotification(Result.success(true))
|
|
||||||
}
|
}
|
||||||
val flagsService = FakeFeatureFlagService(
|
val flagsService = FakeFeatureFlagService(
|
||||||
mapOf(
|
mapOf(
|
||||||
|
|
@ -890,13 +999,10 @@ class MessageComposerPresenterTest {
|
||||||
assertThat(awaitItem().memberSuggestions).isEmpty()
|
assertThat(awaitItem().memberSuggestions).isEmpty()
|
||||||
|
|
||||||
// If user has no permission to send `@room` mentions, `RoomMemberSuggestion.Room` is not returned
|
// If user has no permission to send `@room` mentions, `RoomMemberSuggestion.Room` is not returned
|
||||||
room.givenCanTriggerRoomNotification(Result.success(false))
|
canUserTriggerRoomNotificationResult = false
|
||||||
initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "")))
|
initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "")))
|
||||||
assertThat(awaitItem().memberSuggestions)
|
assertThat(awaitItem().memberSuggestions)
|
||||||
.containsExactly(ResolvedMentionSuggestion.Member(bob), ResolvedMentionSuggestion.Member(david))
|
.containsExactly(ResolvedMentionSuggestion.Member(bob), ResolvedMentionSuggestion.Member(david))
|
||||||
|
|
||||||
// If room is a DM, `RoomMemberSuggestion.Room` is not returned
|
|
||||||
room.givenCanTriggerRoomNotification(Result.success(true))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -910,13 +1016,14 @@ class MessageComposerPresenterTest {
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
activeMemberCount = 2,
|
activeMemberCount = 2,
|
||||||
isEncrypted = true,
|
isEncrypted = true,
|
||||||
|
canUserTriggerRoomNotificationResult = { Result.success(true) },
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomMembersState(
|
givenRoomMembersState(
|
||||||
MatrixRoomMembersState.Ready(
|
MatrixRoomMembersState.Ready(
|
||||||
persistentListOf(currentUser, invitedUser, bob, david),
|
persistentListOf(currentUser, invitedUser, bob, david),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
givenCanTriggerRoomNotification(Result.success(true))
|
|
||||||
}
|
}
|
||||||
val flagsService = FakeFeatureFlagService(
|
val flagsService = FakeFeatureFlagService(
|
||||||
mapOf(
|
mapOf(
|
||||||
|
|
@ -973,7 +1080,14 @@ class MessageComposerPresenterTest {
|
||||||
this.replyMessageLambda = replyMessageLambda
|
this.replyMessageLambda = replyMessageLambda
|
||||||
this.editMessageLambda = editMessageLambda
|
this.editMessageLambda = editMessageLambda
|
||||||
}
|
}
|
||||||
val room = FakeMatrixRoom(liveTimeline = timeline)
|
val sendMessageResult = lambdaRecorder { _: String, _: String?, _: List<Mention> ->
|
||||||
|
Result.success(Unit)
|
||||||
|
}
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
sendMessageResult = sendMessageResult,
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
val presenter = createPresenter(room = room, coroutineScope = this)
|
val presenter = createPresenter(room = room, coroutineScope = this)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -993,7 +1107,8 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID)))
|
sendMessageResult.assertions().isCalledOnce()
|
||||||
|
.with(value(A_MESSAGE), any(), value(listOf(Mention.User(A_USER_ID))))
|
||||||
|
|
||||||
// Check intentional mentions on reply sent
|
// Check intentional mentions on reply sent
|
||||||
initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode()))
|
initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode()))
|
||||||
|
|
@ -1049,22 +1164,32 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle typing notice event`() = runTest {
|
fun `present - handle typing notice event`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val typingNoticeResult = lambdaRecorder<Boolean, Result<Unit>> { Result.success(Unit) }
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = typingNoticeResult
|
||||||
|
)
|
||||||
val presenter = createPresenter(room = room, coroutineScope = this)
|
val presenter = createPresenter(room = room, coroutineScope = this)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitFirstItem()
|
val initialState = awaitFirstItem()
|
||||||
assertThat(room.typingRecord).isEmpty()
|
typingNoticeResult.assertions().isNeverCalled()
|
||||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
||||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
|
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
|
||||||
assertThat(room.typingRecord).isEqualTo(listOf(true, false))
|
typingNoticeResult.assertions().isCalledExactly(2)
|
||||||
|
.withSequence(
|
||||||
|
listOf(value(true)),
|
||||||
|
listOf(value(false)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle typing notice event when sending typing notice is disabled`() = runTest {
|
fun `present - handle typing notice event when sending typing notice is disabled`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val typingNoticeResult = lambdaRecorder<Boolean, Result<Unit>> { Result.success(Unit) }
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = typingNoticeResult
|
||||||
|
)
|
||||||
val store = InMemorySessionPreferencesStore(
|
val store = InMemorySessionPreferencesStore(
|
||||||
isSendTypingNotificationsEnabled = false
|
isSendTypingNotificationsEnabled = false
|
||||||
)
|
)
|
||||||
|
|
@ -1073,10 +1198,10 @@ class MessageComposerPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitFirstItem()
|
val initialState = awaitFirstItem()
|
||||||
assertThat(room.typingRecord).isEmpty()
|
typingNoticeResult.assertions().isNeverCalled()
|
||||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
||||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
|
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
|
||||||
assertThat(room.typingRecord).isEmpty()
|
typingNoticeResult.assertions().isNeverCalled()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1215,7 +1340,10 @@ class MessageComposerPresenterTest {
|
||||||
val timeline = FakeTimeline().apply {
|
val timeline = FakeTimeline().apply {
|
||||||
this.loadReplyDetailsLambda = loadReplyDetailsLambda
|
this.loadReplyDetailsLambda = loadReplyDetailsLambda
|
||||||
}
|
}
|
||||||
val room = FakeMatrixRoom(liveTimeline = timeline)
|
val room = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
typingNoticeResult = { Result.success(Unit) },
|
||||||
|
)
|
||||||
val permalinkBuilder = FakePermalinkBuilder()
|
val permalinkBuilder = FakePermalinkBuilder()
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
|
|
@ -1352,7 +1480,9 @@ class MessageComposerPresenterTest {
|
||||||
|
|
||||||
private fun createPresenter(
|
private fun createPresenter(
|
||||||
coroutineScope: CoroutineScope,
|
coroutineScope: CoroutineScope,
|
||||||
room: MatrixRoom = FakeMatrixRoom(),
|
room: MatrixRoom = FakeMatrixRoom(
|
||||||
|
typingNoticeResult = { Result.success(Unit) }
|
||||||
|
),
|
||||||
pickerProvider: PickerProvider = this.pickerProvider,
|
pickerProvider: PickerProvider = this.pickerProvider,
|
||||||
featureFlagService: FeatureFlagService = this.featureFlagService,
|
featureFlagService: FeatureFlagService = this.featureFlagService,
|
||||||
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
@ -38,9 +39,9 @@ class TimelineControllerTest {
|
||||||
val liveTimeline = FakeTimeline(name = "live")
|
val liveTimeline = FakeTimeline(name = "live")
|
||||||
val detachedTimeline = FakeTimeline(name = "detached")
|
val detachedTimeline = FakeTimeline(name = "detached")
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline
|
liveTimeline = liveTimeline,
|
||||||
|
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||||
)
|
)
|
||||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
|
||||||
val sut = TimelineController(matrixRoom)
|
val sut = TimelineController(matrixRoom)
|
||||||
|
|
||||||
sut.activeTimelineFlow().test {
|
sut.activeTimelineFlow().test {
|
||||||
|
|
@ -68,8 +69,17 @@ class TimelineControllerTest {
|
||||||
val liveTimeline = FakeTimeline(name = "live")
|
val liveTimeline = FakeTimeline(name = "live")
|
||||||
val detachedTimeline1 = FakeTimeline(name = "detached 1")
|
val detachedTimeline1 = FakeTimeline(name = "detached 1")
|
||||||
val detachedTimeline2 = FakeTimeline(name = "detached 2")
|
val detachedTimeline2 = FakeTimeline(name = "detached 2")
|
||||||
|
var callNumber = 0
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline
|
liveTimeline = liveTimeline,
|
||||||
|
timelineFocusedOnEventResult = {
|
||||||
|
callNumber++
|
||||||
|
when (callNumber) {
|
||||||
|
1 -> Result.success(detachedTimeline1)
|
||||||
|
2 -> Result.success(detachedTimeline2)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
val sut = TimelineController(matrixRoom)
|
val sut = TimelineController(matrixRoom)
|
||||||
|
|
||||||
|
|
@ -77,7 +87,6 @@ class TimelineControllerTest {
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state).isEqualTo(liveTimeline)
|
assertThat(state).isEqualTo(liveTimeline)
|
||||||
}
|
}
|
||||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline1))
|
|
||||||
sut.focusOnEvent(AN_EVENT_ID)
|
sut.focusOnEvent(AN_EVENT_ID)
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state).isEqualTo(detachedTimeline1)
|
assertThat(state).isEqualTo(detachedTimeline1)
|
||||||
|
|
@ -85,7 +94,6 @@ class TimelineControllerTest {
|
||||||
assertThat(detachedTimeline1.closeCounter).isEqualTo(0)
|
assertThat(detachedTimeline1.closeCounter).isEqualTo(0)
|
||||||
assertThat(detachedTimeline2.closeCounter).isEqualTo(0)
|
assertThat(detachedTimeline2.closeCounter).isEqualTo(0)
|
||||||
// Focus on another event should close the previous detached timeline
|
// Focus on another event should close the previous detached timeline
|
||||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline2))
|
|
||||||
sut.focusOnEvent(AN_EVENT_ID)
|
sut.focusOnEvent(AN_EVENT_ID)
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state).isEqualTo(detachedTimeline2)
|
assertThat(state).isEqualTo(detachedTimeline2)
|
||||||
|
|
@ -117,11 +125,10 @@ class TimelineControllerTest {
|
||||||
val liveTimeline = FakeTimeline(name = "live")
|
val liveTimeline = FakeTimeline(name = "live")
|
||||||
val detachedTimeline = FakeTimeline(name = "detached")
|
val detachedTimeline = FakeTimeline(name = "detached")
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline
|
liveTimeline = liveTimeline,
|
||||||
|
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||||
)
|
)
|
||||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
|
||||||
val sut = TimelineController(matrixRoom)
|
val sut = TimelineController(matrixRoom)
|
||||||
|
|
||||||
sut.activeTimelineFlow().test {
|
sut.activeTimelineFlow().test {
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state).isEqualTo(liveTimeline)
|
assertThat(state).isEqualTo(liveTimeline)
|
||||||
|
|
@ -168,9 +175,9 @@ class TimelineControllerTest {
|
||||||
sendMessageLambda = lambdaForDetached
|
sendMessageLambda = lambdaForDetached
|
||||||
}
|
}
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline
|
liveTimeline = liveTimeline,
|
||||||
|
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||||
)
|
)
|
||||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
|
||||||
val sut = TimelineController(matrixRoom)
|
val sut = TimelineController(matrixRoom)
|
||||||
sut.activeTimelineFlow().test {
|
sut.activeTimelineFlow().test {
|
||||||
sut.focusOnEvent(AN_EVENT_ID)
|
sut.focusOnEvent(AN_EVENT_ID)
|
||||||
|
|
@ -193,9 +200,9 @@ class TimelineControllerTest {
|
||||||
val liveTimeline = FakeTimeline(name = "live")
|
val liveTimeline = FakeTimeline(name = "live")
|
||||||
val detachedTimeline = FakeTimeline(name = "detached")
|
val detachedTimeline = FakeTimeline(name = "detached")
|
||||||
val matrixRoom = FakeMatrixRoom(
|
val matrixRoom = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline
|
liveTimeline = liveTimeline,
|
||||||
|
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||||
)
|
)
|
||||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
|
||||||
val sut = TimelineController(matrixRoom)
|
val sut = TimelineController(matrixRoom)
|
||||||
|
|
||||||
sut.activeTimelineFlow().test {
|
sut.activeTimelineFlow().test {
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val room = FakeMatrixRoom(liveTimeline = timeline)
|
val room = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val sessionPreferencesStore = InMemorySessionPreferencesStore(isSendPublicReadReceiptsEnabled = false)
|
val sessionPreferencesStore = InMemorySessionPreferencesStore(isSendPublicReadReceiptsEnabled = false)
|
||||||
val presenter = createTimelinePresenter(
|
val presenter = createTimelinePresenter(
|
||||||
timeline = timeline,
|
timeline = timeline,
|
||||||
|
|
@ -482,9 +485,9 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||||
)
|
)
|
||||||
val room = FakeMatrixRoom(
|
val room = FakeMatrixRoom(
|
||||||
liveTimeline = liveTimeline,
|
liveTimeline = liveTimeline,
|
||||||
).apply {
|
timelineFocusedOnEventResult = { Result.success(detachedTimeline) },
|
||||||
givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
}
|
)
|
||||||
val presenter = createTimelinePresenter(
|
val presenter = createTimelinePresenter(
|
||||||
room = room,
|
room = room,
|
||||||
)
|
)
|
||||||
|
|
@ -529,6 +532,7 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
),
|
),
|
||||||
timelineItemIndexer = timelineItemIndexer,
|
timelineItemIndexer = timelineItemIndexer,
|
||||||
)
|
)
|
||||||
|
|
@ -551,9 +555,9 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||||
liveTimeline = FakeTimeline(
|
liveTimeline = FakeTimeline(
|
||||||
timelineItems = flowOf(emptyList()),
|
timelineItems = flowOf(emptyList()),
|
||||||
),
|
),
|
||||||
).apply {
|
timelineFocusedOnEventResult = { Result.failure(Throwable("An error")) },
|
||||||
givenTimelineFocusedOnEventResult(Result.failure(Throwable("An error")))
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
},
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -594,7 +598,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val room = FakeMatrixRoom(liveTimeline = timeline).apply {
|
val room = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Unknown)
|
givenRoomMembersState(MatrixRoomMembersState.Unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -626,7 +633,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||||
|
|
||||||
private fun TestScope.createTimelinePresenter(
|
private fun TestScope.createTimelinePresenter(
|
||||||
timeline: Timeline = FakeTimeline(),
|
timeline: Timeline = FakeTimeline(),
|
||||||
room: FakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline),
|
room: FakeMatrixRoom = FakeMatrixRoom(
|
||||||
|
liveTimeline = timeline,
|
||||||
|
canUserSendMessageResult = { _, _ -> Result.success(true) }
|
||||||
|
),
|
||||||
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(),
|
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(),
|
||||||
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
|
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
|
||||||
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,11 @@ import android.net.Uri
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
|
import android.text.SpannedString
|
||||||
import android.text.style.URLSpan
|
import android.text.style.URLSpan
|
||||||
import androidx.core.text.buildSpannedString
|
import androidx.core.text.buildSpannedString
|
||||||
import androidx.core.text.inSpans
|
import androidx.core.text.inSpans
|
||||||
|
import androidx.core.text.toSpannable
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
||||||
|
|
@ -74,6 +76,7 @@ import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorW
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Assert.fail
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
|
@ -195,7 +198,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
inSpans(URLSpan("https://matrix.org")) {
|
inSpans(URLSpan("https://matrix.org")) {
|
||||||
append("and manually added link")
|
append("and manually added link")
|
||||||
}
|
}
|
||||||
}
|
}.toSpannable()
|
||||||
val sut = createTimelineItemContentMessageFactory(
|
val sut = createTimelineItemContentMessageFactory(
|
||||||
htmlConverterTransform = { expected }
|
htmlConverterTransform = { expected }
|
||||||
)
|
)
|
||||||
|
|
@ -610,7 +613,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
senderDisambiguatedDisplayName = "Bob",
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
)
|
)
|
||||||
assertThat((result as TimelineItemNoticeContent).formattedBody).isEqualTo("formatted")
|
(result as TimelineItemNoticeContent).formattedBody.assertSpannedEquals(SpannedString("formatted"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -644,7 +647,8 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
senderDisambiguatedDisplayName = "Bob",
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
)
|
)
|
||||||
assertThat((result as TimelineItemEmoteContent).formattedBody).isEqualTo(SpannableString("* Bob formatted"))
|
|
||||||
|
(result as TimelineItemEmoteContent).formattedBody.assertSpannedEquals(SpannableString("* Bob formatted"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -654,7 +658,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
inSpans(URLSpan("https://www.example.org")) {
|
inSpans(URLSpan("https://www.example.org")) {
|
||||||
append("me@matrix.org")
|
append("me@matrix.org")
|
||||||
}
|
}
|
||||||
}
|
}.toSpannable()
|
||||||
val sut = createTimelineItemContentMessageFactory(
|
val sut = createTimelineItemContentMessageFactory(
|
||||||
htmlConverterTransform = { expectedSpanned },
|
htmlConverterTransform = { expectedSpanned },
|
||||||
permalinkParser = FakePermalinkParser { PermalinkData.FallbackLink(Uri.EMPTY) }
|
permalinkParser = FakePermalinkParser { PermalinkData.FallbackLink(Uri.EMPTY) }
|
||||||
|
|
@ -669,7 +673,59 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
senderDisambiguatedDisplayName = "Bob",
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
)
|
)
|
||||||
assertThat((result as TimelineItemTextContent).formattedBody).isEqualTo(expectedSpanned)
|
(result as TimelineItemTextContent).formattedBody.assertSpannedEquals(expectedSpanned)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a message with plain URL in a formatted body Spanned format gets linkified too`() = runTest {
|
||||||
|
val expectedSpanned = buildSpannedString {
|
||||||
|
append("Test ")
|
||||||
|
inSpansWithFlags(URLSpan("https://www.example.org"), flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) {
|
||||||
|
append("https://www.example.org")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val sut = createTimelineItemContentMessageFactory(
|
||||||
|
htmlConverterTransform = { expectedSpanned },
|
||||||
|
permalinkParser = FakePermalinkParser { PermalinkData.FallbackLink(Uri.EMPTY) }
|
||||||
|
)
|
||||||
|
val result = sut.create(
|
||||||
|
content = createMessageContent(
|
||||||
|
type = TextMessageType(
|
||||||
|
body = "Test [me@matrix.org](https://www.example.org)",
|
||||||
|
formatted = FormattedBody(MessageFormat.HTML, "Test https://www.example.org")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
|
eventId = AN_EVENT_ID,
|
||||||
|
)
|
||||||
|
(result as TimelineItemTextContent).formattedBody.assertSpannedEquals(expectedSpanned)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a message with plain URL in a formatted body with plain text format gets linkified too`() = runTest {
|
||||||
|
val resultString = "Test https://www.example.org"
|
||||||
|
val expectedSpanned = buildSpannedString {
|
||||||
|
append("Test ")
|
||||||
|
inSpansWithFlags(URLSpan("https://www.example.org"), flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) {
|
||||||
|
append("https://www.example.org")
|
||||||
|
}
|
||||||
|
}.toSpannable()
|
||||||
|
val sut = createTimelineItemContentMessageFactory(
|
||||||
|
htmlConverterTransform = { resultString },
|
||||||
|
permalinkParser = FakePermalinkParser { PermalinkData.FallbackLink(Uri.EMPTY) }
|
||||||
|
)
|
||||||
|
val result = sut.create(
|
||||||
|
content = createMessageContent(
|
||||||
|
type = TextMessageType(
|
||||||
|
body = "Test [me@matrix.org](https://www.example.org)",
|
||||||
|
formatted = FormattedBody(MessageFormat.HTML, "Test https://www.example.org")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
|
eventId = AN_EVENT_ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
(result as TimelineItemTextContent).formattedBody.assertSpannedEquals(expectedSpanned)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMessageContent(
|
private fun createMessageContent(
|
||||||
|
|
@ -718,3 +774,40 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation()
|
fileExtensionExtractor = FileExtensionExtractorWithoutValidation()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun SpannableStringBuilder.inSpansWithFlags(span: Any, flags: Int, action: SpannableStringBuilder.() -> Unit) {
|
||||||
|
val start = this.length
|
||||||
|
action()
|
||||||
|
val end = this.length
|
||||||
|
setSpan(span, start, end, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CharSequence?.assertSpannedEquals(other: CharSequence?) {
|
||||||
|
if (this == null && other == null) {
|
||||||
|
return
|
||||||
|
} else if (this is Spanned && other is Spanned) {
|
||||||
|
assertThat(this.toString()).isEqualTo(other.toString())
|
||||||
|
assertThat(this.length).isEqualTo(other.length)
|
||||||
|
val thisSpans = this.getSpans(0, this.length, Any::class.java)
|
||||||
|
val otherSpans = other.getSpans(0, other.length, Any::class.java)
|
||||||
|
if (thisSpans.size != otherSpans.size) {
|
||||||
|
fail("Expected ${thisSpans.size} spans, got ${otherSpans.size}")
|
||||||
|
}
|
||||||
|
thisSpans.forEachIndexed { index, span ->
|
||||||
|
val otherSpan = otherSpans[index]
|
||||||
|
// URLSpans don't have a proper `equals` implementation, so we compare the URL instead
|
||||||
|
if (span is URLSpan && otherSpan is URLSpan) {
|
||||||
|
assertThat(span.url).isEqualTo(otherSpan.url)
|
||||||
|
} else {
|
||||||
|
assertThat(span).isEqualTo(otherSpan)
|
||||||
|
}
|
||||||
|
assertThat(this.getSpanStart(span)).isEqualTo(other.getSpanStart(otherSpan))
|
||||||
|
assertThat(this.getSpanEnd(span)).isEqualTo(other.getSpanEnd(otherSpan))
|
||||||
|
assertThat(this.getSpanFlags(span)).isEqualTo(other.getSpanFlags(otherSpan))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val thisString = this?.toString() ?: "null"
|
||||||
|
val otherString = other?.toString() ?: "null"
|
||||||
|
fail("Expected Spanned, got $thisString and $otherString")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
|
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
|
||||||
import io.element.android.features.messages.test.FakeMessageComposerContext
|
import io.element.android.features.messages.test.FakeMessageComposerContext
|
||||||
|
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
|
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
|
||||||
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
|
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
|
||||||
|
|
@ -45,6 +47,7 @@ import io.element.android.libraries.textcomposer.model.VoiceMessageState
|
||||||
import io.element.android.libraries.voicerecorder.test.FakeVoiceRecorder
|
import io.element.android.libraries.voicerecorder.test.FakeVoiceRecorder
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -63,7 +66,10 @@ class VoiceMessageComposerPresenterTest {
|
||||||
recordingDuration = RECORDING_DURATION
|
recordingDuration = RECORDING_DURATION
|
||||||
)
|
)
|
||||||
private val analyticsService = FakeAnalyticsService()
|
private val analyticsService = FakeAnalyticsService()
|
||||||
private val matrixRoom = FakeMatrixRoom()
|
private val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> { Result.success(FakeMediaUploadHandler()) }
|
||||||
|
private val matrixRoom = FakeMatrixRoom(
|
||||||
|
sendMediaResult = sendMediaResult
|
||||||
|
)
|
||||||
private val mediaPreProcessor = FakeMediaPreProcessor().apply { givenAudioResult() }
|
private val mediaPreProcessor = FakeMediaPreProcessor().apply { givenAudioResult() }
|
||||||
private val mediaSender = MediaSender(mediaPreProcessor, matrixRoom)
|
private val mediaSender = MediaSender(mediaPreProcessor, matrixRoom)
|
||||||
private val messageComposerContext = FakeMessageComposerContext()
|
private val messageComposerContext = FakeMessageComposerContext()
|
||||||
|
|
@ -295,7 +301,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
|
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||||
|
|
||||||
testPauseAndDestroy(finalState)
|
testPauseAndDestroy(finalState)
|
||||||
|
|
@ -346,7 +352,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
|
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||||
|
|
||||||
testPauseAndDestroy(finalState)
|
testPauseAndDestroy(finalState)
|
||||||
|
|
@ -369,7 +375,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
|
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||||
|
|
||||||
testPauseAndDestroy(finalState)
|
testPauseAndDestroy(finalState)
|
||||||
|
|
@ -393,7 +399,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
|
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState(isSending = true))
|
assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState(isSending = true))
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
sendMediaResult.assertions().isNeverCalled()
|
||||||
assertThat(analyticsService.trackedErrors).hasSize(0)
|
assertThat(analyticsService.trackedErrors).hasSize(0)
|
||||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0)
|
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0)
|
||||||
|
|
||||||
|
|
@ -418,13 +424,13 @@ class VoiceMessageComposerPresenterTest {
|
||||||
|
|
||||||
ensureAllEventsConsumed()
|
ensureAllEventsConsumed()
|
||||||
assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState())
|
assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState())
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
sendMediaResult.assertions().isNeverCalled()
|
||||||
|
|
||||||
mediaPreProcessor.givenAudioResult()
|
mediaPreProcessor.givenAudioResult()
|
||||||
previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
sendMediaResult.assertions().isCalledOnce()
|
||||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||||
|
|
||||||
testPauseAndDestroy(finalState)
|
testPauseAndDestroy(finalState)
|
||||||
|
|
@ -461,7 +467,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
assertThat(showSendFailureDialog).isFalse()
|
assertThat(showSendFailureDialog).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
sendMediaResult.assertions().isNeverCalled()
|
||||||
testPauseAndDestroy(finalState)
|
testPauseAndDestroy(finalState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -477,7 +483,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
initialState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
initialState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
||||||
|
|
||||||
assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
sendMediaResult.assertions().isNeverCalled()
|
||||||
assertThat(analyticsService.trackedErrors).hasSize(1)
|
assertThat(analyticsService.trackedErrors).hasSize(1)
|
||||||
voiceRecorder.assertCalls(started = 0)
|
voiceRecorder.assertCalls(started = 0)
|
||||||
|
|
||||||
|
|
@ -496,7 +502,7 @@ class VoiceMessageComposerPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start))
|
initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start))
|
||||||
|
|
||||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
sendMediaResult.assertions().isNeverCalled()
|
||||||
assertThat(analyticsService.trackedErrors).containsExactly(
|
assertThat(analyticsService.trackedErrors).containsExactly(
|
||||||
VoiceMessageException.PermissionMissing(message = "Expected permission to record but none", cause = exception)
|
VoiceMessageException.PermissionMissing(message = "Expected permission to record but none", cause = exception)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_onboarding_sign_in_manually">"Zaloguj się ręcznie"</string>
|
||||||
|
<string name="screen_onboarding_sign_in_with_qr_code">"Zaloguj się za pomocą kodu QR"</string>
|
||||||
|
<string name="screen_onboarding_sign_up">"Utwórz konto"</string>
|
||||||
|
<string name="screen_onboarding_welcome_message">"Witamy w %1$s. Szybszy i prostszy niż kiedykolwiek."</string>
|
||||||
|
<string name="screen_onboarding_welcome_subtitle">"Witamy w %1$s. Doładowany, dla szybkości i prostoty."</string>
|
||||||
|
<string name="screen_onboarding_welcome_title">"Be in your element"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_onboarding_sign_in_manually">"Iniciar sessão manualmente"</string>
|
||||||
|
<string name="screen_onboarding_sign_in_with_qr_code">"Iniciar sessão com código QR"</string>
|
||||||
|
<string name="screen_onboarding_sign_up">"Criar conta"</string>
|
||||||
|
<string name="screen_onboarding_welcome_message">"Bem-vindo ao mais rápido %1$s de todos os tempos. Turbinado para velocidade e simplicidade."</string>
|
||||||
|
<string name="screen_onboarding_welcome_subtitle">"Bem-vindo ao %1$s. Turbinado, para velocidade e simplicidade"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -4,8 +4,16 @@
|
||||||
<string name="screen_create_poll_anonymous_desc">"შედეგების ჩვენება მხოლოდ გამოკითხვის დასრულების შემდეგ"</string>
|
<string name="screen_create_poll_anonymous_desc">"შედეგების ჩვენება მხოლოდ გამოკითხვის დასრულების შემდეგ"</string>
|
||||||
<string name="screen_create_poll_anonymous_headline">"ხმების დამალვა"</string>
|
<string name="screen_create_poll_anonymous_headline">"ხმების დამალვა"</string>
|
||||||
<string name="screen_create_poll_answer_hint">"ვარიანტი %1$d"</string>
|
<string name="screen_create_poll_answer_hint">"ვარიანტი %1$d"</string>
|
||||||
|
<string name="screen_create_poll_cancel_confirmation_content_android">"თქვენი ცვლილებები არ არის შენახული. დარწმუნებული ხართ, რომ გსურთ დაბრუნება?"</string>
|
||||||
<string name="screen_create_poll_question_desc">"კითხვა ან თემა"</string>
|
<string name="screen_create_poll_question_desc">"კითხვა ან თემა"</string>
|
||||||
<string name="screen_create_poll_question_hint">"რას ეხება გამოკითხვა?"</string>
|
<string name="screen_create_poll_question_hint">"რას ეხება გამოკითხვა?"</string>
|
||||||
<string name="screen_create_poll_title">"გამოკითხვის შექმნა"</string>
|
<string name="screen_create_poll_title">"გამოკითხვის შექმნა"</string>
|
||||||
|
<string name="screen_edit_poll_delete_confirmation">"დარწმუნებული ხართ, რომ გსურთ ამ გამოკითხვის წაშლა?"</string>
|
||||||
|
<string name="screen_edit_poll_delete_confirmation_title">"გამოკითხვის წაშლა"</string>
|
||||||
<string name="screen_edit_poll_title">"გამოკითხვის რედაქტირება"</string>
|
<string name="screen_edit_poll_title">"გამოკითხვის რედაქტირება"</string>
|
||||||
|
<string name="screen_polls_history_empty_ongoing">"მიმდინარე გამოკითხვები ვერ მოიძებნა."</string>
|
||||||
|
<string name="screen_polls_history_empty_past">"ბოლო გამოკითხვების მოძებნა ვერ მოხერხდა."</string>
|
||||||
|
<string name="screen_polls_history_filter_ongoing">"მიმდინარე"</string>
|
||||||
|
<string name="screen_polls_history_filter_past">"წარსული"</string>
|
||||||
|
<string name="screen_polls_history_title">"გამოკითხვები"</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
19
features/poll/impl/src/main/res/values-pl/translations.xml
Normal file
19
features/poll/impl/src/main/res/values-pl/translations.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_create_poll_add_option_btn">"Dodaj opcję"</string>
|
||||||
|
<string name="screen_create_poll_anonymous_desc">"Pokaż wyniki dopiero po zakończeniu ankiety"</string>
|
||||||
|
<string name="screen_create_poll_anonymous_headline">"Ukryj głosy"</string>
|
||||||
|
<string name="screen_create_poll_answer_hint">"Opcja %1$d"</string>
|
||||||
|
<string name="screen_create_poll_cancel_confirmation_content_android">"Twoje zmiany nie zostały zapisane. Czy na pewno chcesz wrócić?"</string>
|
||||||
|
<string name="screen_create_poll_question_desc">"Pytanie lub temat"</string>
|
||||||
|
<string name="screen_create_poll_question_hint">"Czego dotyczy ankieta?"</string>
|
||||||
|
<string name="screen_create_poll_title">"Utwórz ankietę"</string>
|
||||||
|
<string name="screen_edit_poll_delete_confirmation">"Czy na pewno chcesz usunąć tę ankietę?"</string>
|
||||||
|
<string name="screen_edit_poll_delete_confirmation_title">"Usuń ankietę"</string>
|
||||||
|
<string name="screen_edit_poll_title">"Edytuj ankietę"</string>
|
||||||
|
<string name="screen_polls_history_empty_ongoing">"Nie znaleziono ankiet w trakcie."</string>
|
||||||
|
<string name="screen_polls_history_empty_past">"Nie znaleziono ankiet."</string>
|
||||||
|
<string name="screen_polls_history_filter_ongoing">"W trakcie"</string>
|
||||||
|
<string name="screen_polls_history_filter_past">"Przeszłe"</string>
|
||||||
|
<string name="screen_polls_history_title">"Ankiety"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_create_poll_add_option_btn">"Adicionar opção"</string>
|
||||||
|
<string name="screen_create_poll_anonymous_desc">"Mostrar resultados somente após o término da enquete"</string>
|
||||||
|
<string name="screen_create_poll_anonymous_headline">"Ocultar votos"</string>
|
||||||
|
<string name="screen_create_poll_answer_hint">"Opção %1$d"</string>
|
||||||
|
<string name="screen_create_poll_question_desc">"Pergunta ou tópico"</string>
|
||||||
|
<string name="screen_create_poll_question_hint">"Sobre o que é a enquete?"</string>
|
||||||
|
<string name="screen_create_poll_title">"Criar enquete"</string>
|
||||||
|
<string name="screen_edit_poll_delete_confirmation_title">"Excluir Enquete"</string>
|
||||||
|
<string name="screen_edit_poll_title">"Editar enquete"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.SavePollInvocation
|
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
|
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
|
|
@ -51,9 +50,9 @@ import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class) class CreatePollPresenterTest {
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@get:Rule
|
class CreatePollPresenterTest {
|
||||||
val warmUpRule = WarmUpRule()
|
@get:Rule val warmUpRule = WarmUpRule()
|
||||||
|
|
||||||
private val pollEventId = AN_EVENT_ID
|
private val pollEventId = AN_EVENT_ID
|
||||||
private var navUpInvocationsCount = 0
|
private var navUpInvocationsCount = 0
|
||||||
|
|
@ -128,7 +127,13 @@ import org.junit.Test
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `create poll sends a poll start event`() = runTest {
|
fun `create poll sends a poll start event`() = runTest {
|
||||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll)
|
val createPollResult = lambdaRecorder<String, List<String>, Int, PollKind, Result<Unit>> { _, _, _, _ -> Result.success(Unit) }
|
||||||
|
val presenter = createCreatePollPresenter(
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
createPollResult = createPollResult
|
||||||
|
),
|
||||||
|
mode = CreatePollMode.NewPoll,
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -139,15 +144,13 @@ import org.junit.Test
|
||||||
skipItems(3)
|
skipItems(3)
|
||||||
initial.eventSink(CreatePollEvents.Save)
|
initial.eventSink(CreatePollEvents.Save)
|
||||||
delay(1) // Wait for the coroutine to finish
|
delay(1) // Wait for the coroutine to finish
|
||||||
assertThat(fakeMatrixRoom.createPollInvocations.size).isEqualTo(1)
|
createPollResult.assertions().isCalledOnce()
|
||||||
assertThat(fakeMatrixRoom.createPollInvocations.last()).isEqualTo(
|
.with(
|
||||||
SavePollInvocation(
|
value("A question?"),
|
||||||
question = "A question?",
|
value(listOf("Answer 1", "Answer 2")),
|
||||||
answers = listOf("Answer 1", "Answer 2"),
|
value(1),
|
||||||
maxSelections = 1,
|
value(PollKind.Disclosed),
|
||||||
pollKind = PollKind.Disclosed
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2)
|
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2)
|
||||||
assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo(
|
assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo(
|
||||||
Composer(
|
Composer(
|
||||||
|
|
@ -170,8 +173,15 @@ import org.junit.Test
|
||||||
@Test
|
@Test
|
||||||
fun `when poll creation fails, error is tracked`() = runTest {
|
fun `when poll creation fails, error is tracked`() = runTest {
|
||||||
val error = Exception("cause")
|
val error = Exception("cause")
|
||||||
fakeMatrixRoom.givenCreatePollResult(Result.failure(error))
|
val createPollResult = lambdaRecorder<String, List<String>, Int, PollKind, Result<Unit>> { _, _, _, _ ->
|
||||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll)
|
Result.failure(error)
|
||||||
|
}
|
||||||
|
val presenter = createCreatePollPresenter(
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
createPollResult = createPollResult
|
||||||
|
),
|
||||||
|
mode = CreatePollMode.NewPoll,
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -180,7 +190,7 @@ import org.junit.Test
|
||||||
awaitItem().eventSink(CreatePollEvents.SetAnswer(1, "Answer 2"))
|
awaitItem().eventSink(CreatePollEvents.SetAnswer(1, "Answer 2"))
|
||||||
awaitItem().eventSink(CreatePollEvents.Save)
|
awaitItem().eventSink(CreatePollEvents.Save)
|
||||||
delay(1) // Wait for the coroutine to finish
|
delay(1) // Wait for the coroutine to finish
|
||||||
assertThat(fakeMatrixRoom.createPollInvocations).hasSize(1)
|
createPollResult.assertions().isCalledOnce()
|
||||||
assertThat(fakeAnalyticsService.capturedEvents).isEmpty()
|
assertThat(fakeAnalyticsService.capturedEvents).isEmpty()
|
||||||
assertThat(fakeAnalyticsService.trackedErrors).hasSize(1)
|
assertThat(fakeAnalyticsService.trackedErrors).hasSize(1)
|
||||||
assertThat(fakeAnalyticsService.trackedErrors).containsExactly(
|
assertThat(fakeAnalyticsService.trackedErrors).containsExactly(
|
||||||
|
|
@ -252,14 +262,22 @@ import org.junit.Test
|
||||||
@Test
|
@Test
|
||||||
fun `when edit poll fails, error is tracked`() = runTest {
|
fun `when edit poll fails, error is tracked`() = runTest {
|
||||||
val error = Exception("cause")
|
val error = Exception("cause")
|
||||||
|
val editPollResult = lambdaRecorder { _: EventId, _: String, _: List<String>, _: Int, _: PollKind ->
|
||||||
|
Result.failure<Unit>(error)
|
||||||
|
}
|
||||||
|
val presenter = createCreatePollPresenter(
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
editPollResult = editPollResult,
|
||||||
|
liveTimeline = timeline,
|
||||||
|
),
|
||||||
|
mode = CreatePollMode.EditPoll(pollEventId),
|
||||||
|
)
|
||||||
val editPollLambda = lambdaRecorder { _: EventId, _: String, _: List<String>, _: Int, _: PollKind ->
|
val editPollLambda = lambdaRecorder { _: EventId, _: String, _: List<String>, _: Int, _: PollKind ->
|
||||||
Result.failure<Unit>(error)
|
Result.failure<Unit>(error)
|
||||||
}
|
}
|
||||||
timeline.apply {
|
timeline.apply {
|
||||||
this.editPollLambda = editPollLambda
|
this.editPollLambda = editPollLambda
|
||||||
}
|
}
|
||||||
fakeMatrixRoom.givenEditPollResult(Result.failure(error))
|
|
||||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,11 @@
|
||||||
<string name="screen_advanced_settings_share_presence">"Κοινή χρήση παρουσίας"</string>
|
<string name="screen_advanced_settings_share_presence">"Κοινή χρήση παρουσίας"</string>
|
||||||
<string name="screen_advanced_settings_share_presence_description">"Εάν απενεργοποιηθεί, δεν θα μπορείς να στέλνεις ή να λαμβάνεις αποδεικτικά ανάγνωσης ή ειδοποιήσεις πληκτρολόγησης."</string>
|
<string name="screen_advanced_settings_share_presence_description">"Εάν απενεργοποιηθεί, δεν θα μπορείς να στέλνεις ή να λαμβάνεις αποδεικτικά ανάγνωσης ή ειδοποιήσεις πληκτρολόγησης."</string>
|
||||||
<string name="screen_advanced_settings_view_source_description">"Ενεργοποίησε την επιλογή για προβολή πηγής μηνυμάτων στη ροή."</string>
|
<string name="screen_advanced_settings_view_source_description">"Ενεργοποίησε την επιλογή για προβολή πηγής μηνυμάτων στη ροή."</string>
|
||||||
|
<string name="screen_blocked_users_empty">"Δεν έχεις αποκλεισμένους χρήστες"</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_action">"Άρση αποκλεισμού"</string>
|
<string name="screen_blocked_users_unblock_alert_action">"Άρση αποκλεισμού"</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_description">"Θα μπορείς να δεις ξανά όλα τα μηνύματα του."</string>
|
<string name="screen_blocked_users_unblock_alert_description">"Θα μπορείς να δεις ξανά όλα τα μηνύματα του."</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_title">"Κατάργηση αποκλεισμού χρήστη"</string>
|
<string name="screen_blocked_users_unblock_alert_title">"Κατάργηση αποκλεισμού χρήστη"</string>
|
||||||
|
<string name="screen_blocked_users_unblocking">"Άρση αποκλεισμού…"</string>
|
||||||
<string name="screen_edit_profile_display_name">"Εμφανιζόμενο όνομα"</string>
|
<string name="screen_edit_profile_display_name">"Εμφανιζόμενο όνομα"</string>
|
||||||
<string name="screen_edit_profile_display_name_placeholder">"Το εμφανιζόμενο όνομά σου"</string>
|
<string name="screen_edit_profile_display_name_placeholder">"Το εμφανιζόμενο όνομά σου"</string>
|
||||||
<string name="screen_edit_profile_error">"Παρουσιάστηκε ένα άγνωστο σφάλμα και οι πληροφορίες δεν μπορούσαν να αλλάξουν."</string>
|
<string name="screen_edit_profile_error">"Παρουσιάστηκε ένα άγνωστο σφάλμα και οι πληροφορίες δεν μπορούσαν να αλλάξουν."</string>
|
||||||
|
|
@ -45,7 +47,7 @@
|
||||||
<string name="screen_notification_settings_mentions_section_title">"Αναφορές"</string>
|
<string name="screen_notification_settings_mentions_section_title">"Αναφορές"</string>
|
||||||
<string name="screen_notification_settings_mode_all">"Όλα"</string>
|
<string name="screen_notification_settings_mode_all">"Όλα"</string>
|
||||||
<string name="screen_notification_settings_mode_mentions">"Αναφορές"</string>
|
<string name="screen_notification_settings_mode_mentions">"Αναφορές"</string>
|
||||||
<string name="screen_notification_settings_notification_section_title">"Ειδοποιήσε με για"</string>
|
<string name="screen_notification_settings_notification_section_title">"Ειδοποίησέ με για"</string>
|
||||||
<string name="screen_notification_settings_room_mention_label">"Ειδοποίηση για @room"</string>
|
<string name="screen_notification_settings_room_mention_label">"Ειδοποίηση για @room"</string>
|
||||||
<string name="screen_notification_settings_system_notifications_action_required">"Για να λαμβάνεις ειδοποιήσεις, άλλαξε το %1$s ."</string>
|
<string name="screen_notification_settings_system_notifications_action_required">"Για να λαμβάνεις ειδοποιήσεις, άλλαξε το %1$s ."</string>
|
||||||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"ρυθμίσεις συστήματος"</string>
|
<string name="screen_notification_settings_system_notifications_action_required_content_link">"ρυθμίσεις συστήματος"</string>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<string name="screen_advanced_settings_send_read_receipts">"Ricevute di visualizzazione"</string>
|
<string name="screen_advanced_settings_send_read_receipts">"Ricevute di visualizzazione"</string>
|
||||||
<string name="screen_advanced_settings_send_read_receipts_description">"Se disattivato, le tue ricevute di visualizzazione non verranno inviate a nessuno. Riceverai comunque ricevute di visualizzazione da altri utenti."</string>
|
<string name="screen_advanced_settings_send_read_receipts_description">"Se disattivato, le tue ricevute di visualizzazione non verranno inviate a nessuno. Riceverai comunque ricevute di visualizzazione da altri utenti."</string>
|
||||||
<string name="screen_advanced_settings_share_presence">"Condividi presenza online"</string>
|
<string name="screen_advanced_settings_share_presence">"Condividi presenza online"</string>
|
||||||
<string name="screen_advanced_settings_share_presence_description">"Se disattivato, non potrai inviare o ricevere ricevute di visualizzazione o notifiche di scrittura."</string>
|
<string name="screen_advanced_settings_share_presence_description">"Se disattivato, non potrai inviare o ricevere ricevute di lettura o notifiche di scrittura."</string>
|
||||||
<string name="screen_advanced_settings_view_source_description">"Attiva l\'opzione per visualizzare il codice sorgente del messaggio nella conversazione."</string>
|
<string name="screen_advanced_settings_view_source_description">"Attiva l\'opzione per visualizzare il codice sorgente del messaggio nella conversazione."</string>
|
||||||
<string name="screen_blocked_users_empty">"Non hai utenti bloccati"</string>
|
<string name="screen_blocked_users_empty">"Non hai utenti bloccati"</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_action">"Sblocca"</string>
|
<string name="screen_blocked_users_unblock_alert_action">"Sblocca"</string>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
<string name="screen_advanced_settings_element_call_base_url_description">"დააყენეთ საბაზისო URL Element-ის ზარებისათვის."</string>
|
<string name="screen_advanced_settings_element_call_base_url_description">"დააყენეთ საბაზისო URL Element-ის ზარებისათვის."</string>
|
||||||
<string name="screen_advanced_settings_element_call_base_url_validation_error">"არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."</string>
|
<string name="screen_advanced_settings_element_call_base_url_validation_error">"არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."</string>
|
||||||
<string name="screen_advanced_settings_rich_text_editor_description">"გამორთეთ მდიდარი ტექსტის რედაქტორი, რათა ხელით აკრიფოთ Markdown."</string>
|
<string name="screen_advanced_settings_rich_text_editor_description">"გამორთეთ მდიდარი ტექსტის რედაქტორი, რათა ხელით აკრიფოთ Markdown."</string>
|
||||||
|
<string name="screen_advanced_settings_view_source_description">"ჩართეთ ოპცია რათა შეტყობინების წყაროს დროის ისტორია ნახოთ."</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_action">"განბლოკვა"</string>
|
<string name="screen_blocked_users_unblock_alert_action">"განბლოკვა"</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_description">"თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას."</string>
|
<string name="screen_blocked_users_unblock_alert_description">"თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას."</string>
|
||||||
<string name="screen_blocked_users_unblock_alert_title">"Მომხმარებლის განბლოკვა"</string>
|
<string name="screen_blocked_users_unblock_alert_title">"Მომხმარებლის განბლოკვა"</string>
|
||||||
|
|
@ -32,6 +33,8 @@
|
||||||
<string name="screen_notification_settings_enable_notifications">"შეტყობინებების ჩართვა ამ მოწყობილობაზე"</string>
|
<string name="screen_notification_settings_enable_notifications">"შეტყობინებების ჩართვა ამ მოწყობილობაზე"</string>
|
||||||
<string name="screen_notification_settings_failed_fixing_configuration">"კონფიგურაცია არ გამოსწორებულა, გთხოვთ, კვლავ სცადოთ."</string>
|
<string name="screen_notification_settings_failed_fixing_configuration">"კონფიგურაცია არ გამოსწორებულა, გთხოვთ, კვლავ სცადოთ."</string>
|
||||||
<string name="screen_notification_settings_group_chats">"ჯგუფური ჩატები"</string>
|
<string name="screen_notification_settings_group_chats">"ჯგუფური ჩატები"</string>
|
||||||
|
<string name="screen_notification_settings_invite_for_me_label">"მოსაწვევები"</string>
|
||||||
|
<string name="screen_notification_settings_mentions_only_disclaimer">"თქვენი სახლის სერვერი არ უჭერს მხარს ამ პარამეტრს დაშიფრულ ოთახებში, ზოგიერთ ოთახში შეიძლება არ მიიღოთ შეტყობინება."</string>
|
||||||
<string name="screen_notification_settings_mentions_section_title">"ხსენებები"</string>
|
<string name="screen_notification_settings_mentions_section_title">"ხსენებები"</string>
|
||||||
<string name="screen_notification_settings_mode_all">"ყველა"</string>
|
<string name="screen_notification_settings_mode_all">"ყველა"</string>
|
||||||
<string name="screen_notification_settings_mode_mentions">"ხსენებები"</string>
|
<string name="screen_notification_settings_mode_mentions">"ხსენებები"</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string>
|
||||||
|
<string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string>
|
||||||
|
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Wybierz sposób otrzymywania powiadomień"</string>
|
||||||
|
<string name="screen_advanced_settings_developer_mode">"Tryb dewelopera"</string>
|
||||||
|
<string name="screen_advanced_settings_developer_mode_description">"Włącz, aby uzyskać dostęp do funkcji dla deweloperów."</string>
|
||||||
|
<string name="screen_advanced_settings_element_call_base_url">"Własny bazowy URL dla połączeń Element"</string>
|
||||||
|
<string name="screen_advanced_settings_element_call_base_url_description">"Ustaw własny bazowy URL dla połączeń Element"</string>
|
||||||
|
<string name="screen_advanced_settings_element_call_base_url_validation_error">"Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."</string>
|
||||||
|
<string name="screen_advanced_settings_rich_text_editor_description">"Wyłącz edytor tekstu bogatego, aby pisać tekst Markdown ręcznie."</string>
|
||||||
|
<string name="screen_advanced_settings_view_source_description">"Włącz opcję, aby wyświetlić źródło wiadomości na osi czasu."</string>
|
||||||
|
<string name="screen_blocked_users_unblock_alert_action">"Odblokuj"</string>
|
||||||
|
<string name="screen_blocked_users_unblock_alert_description">"Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika."</string>
|
||||||
|
<string name="screen_blocked_users_unblock_alert_title">"Odblokuj użytkownika"</string>
|
||||||
|
<string name="screen_edit_profile_display_name">"Wyświetlana nazwa"</string>
|
||||||
|
<string name="screen_edit_profile_display_name_placeholder">"Twoja wyświetlana nazwa"</string>
|
||||||
|
<string name="screen_edit_profile_error">"Wystąpił nieznany błąd przez co nie można było zmienić informacji."</string>
|
||||||
|
<string name="screen_edit_profile_error_title">"Nie można zaktualizować profilu"</string>
|
||||||
|
<string name="screen_edit_profile_title">"Edytuj profil"</string>
|
||||||
|
<string name="screen_edit_profile_updating_details">"Aktualizowanie profilu…"</string>
|
||||||
|
<string name="screen_notification_settings_additional_settings_section_title">"Dodatkowe ustawienia"</string>
|
||||||
|
<string name="screen_notification_settings_calls_label">"Połączenia audio i wideo"</string>
|
||||||
|
<string name="screen_notification_settings_configuration_mismatch">"Niezgodność konfiguracji"</string>
|
||||||
|
<string name="screen_notification_settings_configuration_mismatch_description">"Uprościliśmy Ustawienia powiadomień, aby ułatwić nawigowanie między opcjami. Niektóre ustawienia, które wybrałeś mogły zniknąć, lecz są wciąż aktywne.
|
||||||
|
|
||||||
|
Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz."</string>
|
||||||
|
<string name="screen_notification_settings_direct_chats">"Czaty prywatne"</string>
|
||||||
|
<string name="screen_notification_settings_edit_custom_settings_section_title">"Ustawienia własne wybranego czatu"</string>
|
||||||
|
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Wystąpił błąd podczas aktualizacji ustawienia powiadomień."</string>
|
||||||
|
<string name="screen_notification_settings_edit_mode_all_messages">"Wszystkie wiadomości"</string>
|
||||||
|
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Tylko wzmianki i słowa kluczowe"</string>
|
||||||
|
<string name="screen_notification_settings_edit_screen_direct_section_header">"Na czatach prywatnych, powiadamiaj mnie przez"</string>
|
||||||
|
<string name="screen_notification_settings_edit_screen_group_section_header">"Na czatach grupowych powiadamiaj mnie przez"</string>
|
||||||
|
<string name="screen_notification_settings_enable_notifications">"Włącz powiadomienia na tym urządzeniu"</string>
|
||||||
|
<string name="screen_notification_settings_failed_fixing_configuration">"Konfiguracja nie została poprawiona, spróbuj ponownie."</string>
|
||||||
|
<string name="screen_notification_settings_group_chats">"Czaty grupowe"</string>
|
||||||
|
<string name="screen_notification_settings_invite_for_me_label">"Zaproszenia"</string>
|
||||||
|
<string name="screen_notification_settings_mentions_only_disclaimer">"Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi."</string>
|
||||||
|
<string name="screen_notification_settings_mentions_section_title">"Wzmianki"</string>
|
||||||
|
<string name="screen_notification_settings_mode_all">"Wszystkie"</string>
|
||||||
|
<string name="screen_notification_settings_mode_mentions">"Wzmianki"</string>
|
||||||
|
<string name="screen_notification_settings_notification_section_title">"Powiadamiaj mnie przez"</string>
|
||||||
|
<string name="screen_notification_settings_room_mention_label">"Powiadom mnie na @pokój"</string>
|
||||||
|
<string name="screen_notification_settings_system_notifications_action_required">"Aby otrzymywać powiadomienia, zmień swoje%1$s ."</string>
|
||||||
|
<string name="screen_notification_settings_system_notifications_action_required_content_link">"ustawienia systemowe"</string>
|
||||||
|
<string name="screen_notification_settings_system_notifications_turned_off">"Powiadomienia systemowe wyłączone"</string>
|
||||||
|
<string name="screen_notification_settings_title">"Powiadomienia"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Escolha como receber notificações"</string>
|
||||||
|
<string name="screen_advanced_settings_developer_mode">"Modo de desenvolvedor"</string>
|
||||||
|
<string name="screen_advanced_settings_developer_mode_description">"Habilite para ter acesso a recursos e funcionalidades para desenvolvedores."</string>
|
||||||
|
<string name="screen_advanced_settings_rich_text_editor_description">"Desative o editor de rich text para digitar Markdown manualmente."</string>
|
||||||
|
<string name="screen_blocked_users_unblock_alert_action">"Desbloquear"</string>
|
||||||
|
<string name="screen_blocked_users_unblock_alert_description">"Você poderá ver todas as mensagens deles novamente."</string>
|
||||||
|
<string name="screen_blocked_users_unblock_alert_title">"Desbloquear usuário"</string>
|
||||||
|
<string name="screen_edit_profile_display_name">"Nome de exibição"</string>
|
||||||
|
<string name="screen_edit_profile_display_name_placeholder">"Seu nome de exibição"</string>
|
||||||
|
<string name="screen_edit_profile_error">"Um erro desconhecido foi encontrado e as informações não puderam ser alteradas."</string>
|
||||||
|
<string name="screen_edit_profile_error_title">"Não foi possível atualizar o perfil"</string>
|
||||||
|
<string name="screen_edit_profile_title">"Editar perfil"</string>
|
||||||
|
<string name="screen_edit_profile_updating_details">"Atualizando o perfil…"</string>
|
||||||
|
<string name="screen_notification_settings_additional_settings_section_title">"Configurações adicionais"</string>
|
||||||
|
<string name="screen_notification_settings_calls_label">"Chamadas de áudio e vídeo"</string>
|
||||||
|
<string name="screen_notification_settings_configuration_mismatch">"Incompatibilidade de configuração"</string>
|
||||||
|
<string name="screen_notification_settings_configuration_mismatch_description">"Simplificamos as configurações de notificações para facilitar a localização das opções. Algumas configurações personalizadas que você escolheu no passado não são mostradas aqui, mas ainda estão ativas.
|
||||||
|
|
||||||
|
Se você continuar, algumas de suas configurações poderão mudar."</string>
|
||||||
|
<string name="screen_notification_settings_direct_chats">"Conversas privadas"</string>
|
||||||
|
<string name="screen_notification_settings_edit_custom_settings_section_title">"Configuração personalizada por chat"</string>
|
||||||
|
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Ocorreu um erro ao atualizar a configuração de notificação."</string>
|
||||||
|
<string name="screen_notification_settings_edit_mode_all_messages">"Todas as mensagens"</string>
|
||||||
|
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Somente menções e palavras-chave"</string>
|
||||||
|
<string name="screen_notification_settings_edit_screen_direct_section_header">"Em conversas privadas, me notifique para"</string>
|
||||||
|
<string name="screen_notification_settings_edit_screen_group_section_header">"Em conversas em grupos, me notifique para"</string>
|
||||||
|
<string name="screen_notification_settings_enable_notifications">"Ativar notificações neste dispositivo"</string>
|
||||||
|
<string name="screen_notification_settings_failed_fixing_configuration">"A configuração não foi corrigida, tente novamente."</string>
|
||||||
|
<string name="screen_notification_settings_group_chats">"Bate-papos em grupo"</string>
|
||||||
|
<string name="screen_notification_settings_mentions_section_title">"Menções"</string>
|
||||||
|
<string name="screen_notification_settings_mode_all">"Todos"</string>
|
||||||
|
<string name="screen_notification_settings_mode_mentions">"Menções"</string>
|
||||||
|
<string name="screen_notification_settings_notification_section_title">"Me notifique para"</string>
|
||||||
|
<string name="screen_notification_settings_room_mention_label">"Notifique-me em @room"</string>
|
||||||
|
<string name="screen_notification_settings_system_notifications_action_required_content_link">"configurações do sistema"</string>
|
||||||
|
<string name="screen_notification_settings_system_notifications_turned_off">"Notificações do sistema desativadas"</string>
|
||||||
|
<string name="screen_notification_settings_title">"Notificações"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="crash_detection_dialog_content">"%1$s uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"</string>
|
||||||
|
<string name="rageshake_detection_dialog_content">"Wygląda na to, że potrząsasz telefonem z frustracji. Czy chcesz otworzyć ekran zgłaszania błędów?"</string>
|
||||||
|
<string name="settings_rageshake">"Gniewne wstrząsanie"</string>
|
||||||
|
<string name="settings_rageshake_detection_threshold">"Próg wykrywania"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="crash_detection_dialog_content">"%1$s fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"</string>
|
||||||
|
<string name="rageshake_detection_dialog_content">"Você parece estar sacudindo o telefone em sinal de frustração. Você gostaria de abrir a tela de relatório de erros?"</string>
|
||||||
|
<string name="settings_rageshake">"Rageshake"</string>
|
||||||
|
<string name="settings_rageshake_detection_threshold">"Limiar de deteção"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_bug_report_attach_screenshot">"Dołącz zrzut ekranu"</string>
|
||||||
|
<string name="screen_bug_report_contact_me">"Możecie skontaktować się ze mną, jeśli macie jakiekolwiek dodatkowe pytania."</string>
|
||||||
|
<string name="screen_bug_report_contact_me_title">"Napisz do mnie"</string>
|
||||||
|
<string name="screen_bug_report_edit_screenshot">"Edytuj zrzut ekranu"</string>
|
||||||
|
<string name="screen_bug_report_editor_description">"Opisz problem. Co zrobiłeś? Czego oczekiwałeś? Co się stało zamiast tego. Podaj jak najwięcej szczegółów."</string>
|
||||||
|
<string name="screen_bug_report_editor_placeholder">"Opisz problem…"</string>
|
||||||
|
<string name="screen_bug_report_editor_supporting">"Jeśli to możliwe, napisz zgłoszenje w języku angielskim."</string>
|
||||||
|
<string name="screen_bug_report_include_crash_logs">"Wyślij logi awarii"</string>
|
||||||
|
<string name="screen_bug_report_include_logs">"Zezwól na logi"</string>
|
||||||
|
<string name="screen_bug_report_include_screenshot">"Wyślij zrzut ekranu"</string>
|
||||||
|
<string name="screen_bug_report_logs_description">"Logi zostaną dołączone do Twojej wiadomości, aby upewnić się, że wszystko działa poprawnie. Aby wysłać wiadomość bez logów, wyłącz to ustawienie."</string>
|
||||||
|
<string name="screen_bug_report_rash_logs_alert_title">"%1$s uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"</string>
|
||||||
|
<string name="screen_bug_report_view_logs">"Wyświetl logi"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_bug_report_attach_screenshot">"Anexar captura de tela"</string>
|
||||||
|
<string name="screen_bug_report_contact_me">"Você pode entrar em contato comigo se tiver alguma pergunta adicional."</string>
|
||||||
|
<string name="screen_bug_report_contact_me_title">"Entre em contato comigo"</string>
|
||||||
|
<string name="screen_bug_report_edit_screenshot">"Editar captura de tela"</string>
|
||||||
|
<string name="screen_bug_report_editor_description">"Descreva o problema. O que você fez? O que você esperava que acontecesse? O que realmente aconteceu? Por favor, forneça o máximo de detalhes possível."</string>
|
||||||
|
<string name="screen_bug_report_editor_placeholder">"Descreva o problema…"</string>
|
||||||
|
<string name="screen_bug_report_editor_supporting">"Se possível, escreva a descrição em inglês."</string>
|
||||||
|
<string name="screen_bug_report_include_crash_logs">"Enviar registros de falhas"</string>
|
||||||
|
<string name="screen_bug_report_include_logs">"Permitir registros"</string>
|
||||||
|
<string name="screen_bug_report_include_screenshot">"Enviar captura de tela"</string>
|
||||||
|
<string name="screen_bug_report_logs_description">"Os registros serão incluídos com sua mensagem para garantir que tudo esteja funcionando corretamente. Para enviar sua mensagem sem registros, desative essa configuração."</string>
|
||||||
|
<string name="screen_bug_report_rash_logs_alert_title">"%1$s fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
<string name="screen_notification_settings_mentions_only_disclaimer">"Ο οικιακός διακομιστής σου δεν υποστηρίζει αυτήν την επιλογή σε κρυπτογραφημένα δωμάτια, ενδέχεται να μην λάβεις ειδοποίηση σε ορισμένα δωμάτια."</string>
|
<string name="screen_notification_settings_mentions_only_disclaimer">"Ο οικιακός διακομιστής σου δεν υποστηρίζει αυτήν την επιλογή σε κρυπτογραφημένα δωμάτια, ενδέχεται να μην λάβεις ειδοποίηση σε ορισμένα δωμάτια."</string>
|
||||||
<string name="screen_polls_history_title">"Δημοσκοπήσεις"</string>
|
<string name="screen_polls_history_title">"Δημοσκοπήσεις"</string>
|
||||||
<string name="screen_room_change_permissions_administrators">"Μόνο διαχειριστές"</string>
|
<string name="screen_room_change_permissions_administrators">"Μόνο διαχειριστές"</string>
|
||||||
|
<string name="screen_room_change_permissions_ban_people">"Αποκλεισμός ατόμων"</string>
|
||||||
<string name="screen_room_change_permissions_delete_messages">"Αφαίρεση μηνυμάτων"</string>
|
<string name="screen_room_change_permissions_delete_messages">"Αφαίρεση μηνυμάτων"</string>
|
||||||
<string name="screen_room_change_permissions_everyone">"Όλοι"</string>
|
<string name="screen_room_change_permissions_everyone">"Όλοι"</string>
|
||||||
<string name="screen_room_change_permissions_invite_people">"Πρόσκληση ατόμων"</string>
|
<string name="screen_room_change_permissions_invite_people">"Πρόσκληση ατόμων"</string>
|
||||||
|
|
@ -58,17 +59,23 @@
|
||||||
<string name="screen_room_details_title">"Πληροφορίες δωματίου"</string>
|
<string name="screen_room_details_title">"Πληροφορίες δωματίου"</string>
|
||||||
<string name="screen_room_details_topic_title">"Θέμα"</string>
|
<string name="screen_room_details_topic_title">"Θέμα"</string>
|
||||||
<string name="screen_room_details_updating_room">"Ενημέρωση δωματίου…"</string>
|
<string name="screen_room_details_updating_room">"Ενημέρωση δωματίου…"</string>
|
||||||
|
<string name="screen_room_member_list_ban_member_confirmation_action">"Αποκλεισμός"</string>
|
||||||
<string name="screen_room_member_list_ban_member_confirmation_description">"Δεν θα μπορεί να συμμετέχει ξανά σε αυτό το δωμάτιο εάν προσκληθεί."</string>
|
<string name="screen_room_member_list_ban_member_confirmation_description">"Δεν θα μπορεί να συμμετέχει ξανά σε αυτό το δωμάτιο εάν προσκληθεί."</string>
|
||||||
|
<string name="screen_room_member_list_ban_member_confirmation_title">"Θες σίγουρα να αποκλείσεις αυτό το μέλος;"</string>
|
||||||
|
<string name="screen_room_member_list_banned_empty">"Δεν υπάρχουν αποκλεισμένοι χρήστες σε αυτό το δωμάτιο."</string>
|
||||||
|
<string name="screen_room_member_list_banning_user">"Αποκλεισμός του χρήστη %1$s"</string>
|
||||||
<plurals name="screen_room_member_list_header_title">
|
<plurals name="screen_room_member_list_header_title">
|
||||||
<item quantity="one">"%1$d άτομο"</item>
|
<item quantity="one">"%1$d άτομο"</item>
|
||||||
<item quantity="other">"%1$d άτομα"</item>
|
<item quantity="other">"%1$d άτομα"</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="screen_room_member_list_manage_member_ban">"Αφαίρεση και αποκλεισμός μέλους"</string>
|
||||||
<string name="screen_room_member_list_manage_member_remove">"Αφαίρεση από το δωμάτιο"</string>
|
<string name="screen_room_member_list_manage_member_remove">"Αφαίρεση από το δωμάτιο"</string>
|
||||||
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Αφαίρεση και αποκλεισμός μέλους"</string>
|
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Αφαίρεση και αποκλεισμός μέλους"</string>
|
||||||
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Μόνο αφαίρεση μέλους"</string>
|
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Μόνο αφαίρεση μέλους"</string>
|
||||||
<string name="screen_room_member_list_manage_member_remove_confirmation_title">"Αφαίρεση μέλους και απαγόρευση συμμετοχής στο μέλλον;"</string>
|
<string name="screen_room_member_list_manage_member_remove_confirmation_title">"Αφαίρεση μέλους και απαγόρευση συμμετοχής στο μέλλον;"</string>
|
||||||
<string name="screen_room_member_list_manage_member_unban_action">"Αναίρεση αποκλεισμού"</string>
|
<string name="screen_room_member_list_manage_member_unban_action">"Αναίρεση αποκλεισμού"</string>
|
||||||
<string name="screen_room_member_list_manage_member_unban_message">"Θα μπορεί να συμμετάσχει ξανά στο δωμάτιο εάν προσκληθεί."</string>
|
<string name="screen_room_member_list_manage_member_unban_message">"Θα μπορεί να συμμετάσχει ξανά στο δωμάτιο εάν προσκληθεί."</string>
|
||||||
|
<string name="screen_room_member_list_manage_member_unban_title">"Άρση αποκλεισμού χρήστη"</string>
|
||||||
<string name="screen_room_member_list_manage_member_user_info">"Προβολή προφίλ"</string>
|
<string name="screen_room_member_list_manage_member_user_info">"Προβολή προφίλ"</string>
|
||||||
<string name="screen_room_member_list_mode_banned">"Αποκλεισμένοι"</string>
|
<string name="screen_room_member_list_mode_banned">"Αποκλεισμένοι"</string>
|
||||||
<string name="screen_room_member_list_mode_members">"Μέλη"</string>
|
<string name="screen_room_member_list_mode_members">"Μέλη"</string>
|
||||||
|
|
@ -77,6 +84,7 @@
|
||||||
<string name="screen_room_member_list_role_administrator">"Διαχειριστής"</string>
|
<string name="screen_room_member_list_role_administrator">"Διαχειριστής"</string>
|
||||||
<string name="screen_room_member_list_role_moderator">"Συντονιστής"</string>
|
<string name="screen_room_member_list_role_moderator">"Συντονιστής"</string>
|
||||||
<string name="screen_room_member_list_room_members_header_title">"Μέλη δωματίου"</string>
|
<string name="screen_room_member_list_room_members_header_title">"Μέλη δωματίου"</string>
|
||||||
|
<string name="screen_room_member_list_unbanning_user">"Άρση αποκλεισμού %1$s"</string>
|
||||||
<string name="screen_room_notification_settings_allow_custom">"Να επιτρέπεται η προσαρμοσμένη ρύθμιση"</string>
|
<string name="screen_room_notification_settings_allow_custom">"Να επιτρέπεται η προσαρμοσμένη ρύθμιση"</string>
|
||||||
<string name="screen_room_notification_settings_allow_custom_footnote">"Η ενεργοποίηση αυτής της ρύθμισης θα παρακάμψει την προεπιλεγμένη ρύθμιση"</string>
|
<string name="screen_room_notification_settings_allow_custom_footnote">"Η ενεργοποίηση αυτής της ρύθμισης θα παρακάμψει την προεπιλεγμένη ρύθμιση"</string>
|
||||||
<string name="screen_room_notification_settings_custom_settings_title">"Ειδοποιήσε με σε αυτήν τη συνομιλία για"</string>
|
<string name="screen_room_notification_settings_custom_settings_title">"Ειδοποιήσε με σε αυτήν τη συνομιλία για"</string>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<string name="screen_notification_settings_edit_failed_updating_default_mode">"შეტყობინებების პარამეტრის განახლებისას მოხდა შეცდომა."</string>
|
<string name="screen_notification_settings_edit_failed_updating_default_mode">"შეტყობინებების პარამეტრის განახლებისას მოხდა შეცდომა."</string>
|
||||||
|
<string name="screen_notification_settings_mentions_only_disclaimer">"თქვენი სახლის სერვერი არ უჭერს მხარს ამ პარამეტრს დაშიფრულ ოთახებში, ზოგიერთ ოთახში შეიძლება არ მიიღოთ შეტყობინება."</string>
|
||||||
|
<string name="screen_polls_history_title">"გამოკითხვები"</string>
|
||||||
<string name="screen_room_change_permissions_everyone">"ყველა"</string>
|
<string name="screen_room_change_permissions_everyone">"ყველა"</string>
|
||||||
<string name="screen_room_details_add_topic_title">"თემის დამატება"</string>
|
<string name="screen_room_details_add_topic_title">"თემის დამატება"</string>
|
||||||
<string name="screen_room_details_already_a_member">"უკვე წევრია"</string>
|
<string name="screen_room_details_already_a_member">"უკვე წევრია"</string>
|
||||||
|
|
@ -39,6 +41,7 @@
|
||||||
<string name="screen_room_notification_settings_error_loading_settings">"შეტყობინებების პარამეტრების ჩატვირთვისას მოხდა შეცდომა."</string>
|
<string name="screen_room_notification_settings_error_loading_settings">"შეტყობინებების პარამეტრების ჩატვირთვისას მოხდა შეცდომა."</string>
|
||||||
<string name="screen_room_notification_settings_error_restoring_default">"ნაგულისხმევი რეჟიმის აღდგენა ვერ მოხერხდა, გთხოვთ, სცადოთ ხელახლა."</string>
|
<string name="screen_room_notification_settings_error_restoring_default">"ნაგულისხმევი რეჟიმის აღდგენა ვერ მოხერხდა, გთხოვთ, სცადოთ ხელახლა."</string>
|
||||||
<string name="screen_room_notification_settings_error_setting_mode">"რეჟიმის დაყენება ვერ მოხერხდა, გთხოვთ, სცადოთ ხელახლა."</string>
|
<string name="screen_room_notification_settings_error_setting_mode">"რეჟიმის დაყენება ვერ მოხერხდა, გთხოვთ, სცადოთ ხელახლა."</string>
|
||||||
|
<string name="screen_room_notification_settings_mentions_only_disclaimer">"თქვენი სახლის სერვერი არ უჭერს მხარს ამ პარამეტრს დაშიფრულ ოთახებში, თქვენ არ მიიღებთ შეტყობინებას ამ ოთახში."</string>
|
||||||
<string name="screen_room_notification_settings_mode_all_messages">"ყველა შეტყობინება"</string>
|
<string name="screen_room_notification_settings_mode_all_messages">"ყველა შეტყობინება"</string>
|
||||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"მხოლოდ ხსენებები და საკვანძო სიტყვები"</string>
|
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"მხოლოდ ხსენებები და საკვანძო სიტყვები"</string>
|
||||||
<string name="screen_room_notification_settings_room_custom_settings_title">"ამ ოთახში, შემატყობინეთ:"</string>
|
<string name="screen_room_notification_settings_room_custom_settings_title">"ამ ოთახში, შემატყობინეთ:"</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Wystąpił błąd podczas aktualizacji ustawienia powiadomień."</string>
|
||||||
|
<string name="screen_notification_settings_mentions_only_disclaimer">"Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi."</string>
|
||||||
|
<string name="screen_polls_history_title">"Ankiety"</string>
|
||||||
|
<string name="screen_room_change_permissions_everyone">"Wszyscy"</string>
|
||||||
|
<string name="screen_room_details_add_topic_title">"Dodaj temat"</string>
|
||||||
|
<string name="screen_room_details_already_a_member">"Jest już członkiem"</string>
|
||||||
|
<string name="screen_room_details_already_invited">"Już zaproszony"</string>
|
||||||
|
<string name="screen_room_details_edit_room_title">"Edytuj pokój"</string>
|
||||||
|
<string name="screen_room_details_edition_error">"Wystąpił nieznany błąd i nie można było zmienić informacji."</string>
|
||||||
|
<string name="screen_room_details_edition_error_title">"Nie można zaktualizować pokoju"</string>
|
||||||
|
<string name="screen_room_details_encryption_enabled_subtitle">"Wiadomości są zabezpieczone kłódkami. Tylko Ty i odbiorcy macie unikalne klucze do ich odblokowania."</string>
|
||||||
|
<string name="screen_room_details_encryption_enabled_title">"Szyfrowanie wiadomości włączone"</string>
|
||||||
|
<string name="screen_room_details_error_loading_notification_settings">"Wystąpił błąd podczas ładowania ustawień powiadomień."</string>
|
||||||
|
<string name="screen_room_details_error_muting">"Wyciszenie tego pokoju nie powiodło się, spróbuj ponownie."</string>
|
||||||
|
<string name="screen_room_details_error_unmuting">"Nie udało się wyłączyć wyciszenia tego pokoju. Spróbuj ponownie."</string>
|
||||||
|
<string name="screen_room_details_invite_people_title">"Zaproś znajomych"</string>
|
||||||
|
<string name="screen_room_details_leave_conversation_title">"Opuść rozmowę"</string>
|
||||||
|
<string name="screen_room_details_leave_room_title">"Opuść pokój"</string>
|
||||||
|
<string name="screen_room_details_notification_mode_custom">"Niestandardowy"</string>
|
||||||
|
<string name="screen_room_details_notification_mode_default">"Domyślny"</string>
|
||||||
|
<string name="screen_room_details_notification_title">"Powiadomienia"</string>
|
||||||
|
<string name="screen_room_details_room_name_label">"Nazwa pokoju"</string>
|
||||||
|
<string name="screen_room_details_security_title">"Bezpieczeństwo"</string>
|
||||||
|
<string name="screen_room_details_share_room_title">"Udostępnij pokój"</string>
|
||||||
|
<string name="screen_room_details_topic_title">"Temat"</string>
|
||||||
|
<string name="screen_room_details_updating_room">"Aktualizuję pokój…"</string>
|
||||||
|
<plurals name="screen_room_member_list_header_title">
|
||||||
|
<item quantity="one">"%1$d osoba"</item>
|
||||||
|
<item quantity="few">"%1$d osoby"</item>
|
||||||
|
<item quantity="many">"%1$d osób"</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="screen_room_member_list_pending_header_title">"Oczekiwanie"</string>
|
||||||
|
<string name="screen_room_member_list_room_members_header_title">"Członkowie pokoju"</string>
|
||||||
|
<string name="screen_room_notification_settings_allow_custom">"Zezwalaj na ustawienia niestandardowe"</string>
|
||||||
|
<string name="screen_room_notification_settings_allow_custom_footnote">"Włączenie tej opcji nadpisze ustawienie domyślne"</string>
|
||||||
|
<string name="screen_room_notification_settings_custom_settings_title">"Powiadamiaj mnie o tym czacie przez"</string>
|
||||||
|
<string name="screen_room_notification_settings_default_setting_footnote">"Możesz to zmienić w swoim %1$s."</string>
|
||||||
|
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"ustawienia globalne"</string>
|
||||||
|
<string name="screen_room_notification_settings_default_setting_title">"Ustawienie domyślne"</string>
|
||||||
|
<string name="screen_room_notification_settings_edit_remove_setting">"Usuń ustawienia własne"</string>
|
||||||
|
<string name="screen_room_notification_settings_error_loading_settings">"Wystąpił błąd podczas ładowania ustawień powiadomień."</string>
|
||||||
|
<string name="screen_room_notification_settings_error_restoring_default">"Nie udało się przywrócić trybu domyślnego, spróbuj ponownie."</string>
|
||||||
|
<string name="screen_room_notification_settings_error_setting_mode">"Nie udało się ustawić trybu, spróbuj ponownie."</string>
|
||||||
|
<string name="screen_room_notification_settings_mentions_only_disclaimer">"Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z tego pokoju."</string>
|
||||||
|
<string name="screen_room_notification_settings_mode_all_messages">"Wszystkie wiadomości"</string>
|
||||||
|
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Tylko wzmianki i słowa kluczowe"</string>
|
||||||
|
<string name="screen_room_notification_settings_room_custom_settings_title">"W tym pokoju, powiadamiaj mnie przez"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Ocorreu um erro ao atualizar a configuração de notificação."</string>
|
||||||
|
<string name="screen_room_change_permissions_everyone">"Todos"</string>
|
||||||
|
<string name="screen_room_details_add_topic_title">"Adicionar tópico"</string>
|
||||||
|
<string name="screen_room_details_already_a_member">"Já é membro"</string>
|
||||||
|
<string name="screen_room_details_already_invited">"Já foi convidado"</string>
|
||||||
|
<string name="screen_room_details_edit_room_title">"Editar sala"</string>
|
||||||
|
<string name="screen_room_details_edition_error">"Ocorreu um erro desconhecido e as informações não puderam ser alteradas."</string>
|
||||||
|
<string name="screen_room_details_edition_error_title">"Não foi possível atualizar a sala"</string>
|
||||||
|
<string name="screen_room_details_encryption_enabled_subtitle">"As mensagens são protegidas com bloqueios. Somente você e os destinatários têm as chaves exclusivas para desbloqueá-los."</string>
|
||||||
|
<string name="screen_room_details_encryption_enabled_title">"Criptografia de mensagens ativada"</string>
|
||||||
|
<string name="screen_room_details_error_loading_notification_settings">"Ocorreu um erro ao carregar as configurações de notificação."</string>
|
||||||
|
<string name="screen_room_details_error_muting">"Falha ao silenciar esta sala, tente novamente."</string>
|
||||||
|
<string name="screen_room_details_error_unmuting">"Falha ao ativar o som desta sala. Tente novamente."</string>
|
||||||
|
<string name="screen_room_details_invite_people_title">"Convidar pessoas"</string>
|
||||||
|
<string name="screen_room_details_leave_conversation_title">"Sair da conversa"</string>
|
||||||
|
<string name="screen_room_details_leave_room_title">"Sair da sala"</string>
|
||||||
|
<string name="screen_room_details_notification_mode_custom">"Personalizado"</string>
|
||||||
|
<string name="screen_room_details_notification_mode_default">"Padrão"</string>
|
||||||
|
<string name="screen_room_details_notification_title">"Notificações"</string>
|
||||||
|
<string name="screen_room_details_room_name_label">"Nome da sala"</string>
|
||||||
|
<string name="screen_room_details_security_title">"Segurança"</string>
|
||||||
|
<string name="screen_room_details_share_room_title">"Compartilhar sala"</string>
|
||||||
|
<string name="screen_room_details_topic_title">"Tópico"</string>
|
||||||
|
<string name="screen_room_details_updating_room">"Atualizando a sala…"</string>
|
||||||
|
<plurals name="screen_room_member_list_header_title">
|
||||||
|
<item quantity="one">"%1$d pessoa"</item>
|
||||||
|
<item quantity="other">"%1$d pessoas"</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="screen_room_member_list_pending_header_title">"Pendente"</string>
|
||||||
|
<string name="screen_room_member_list_room_members_header_title">"Membros da sala"</string>
|
||||||
|
<string name="screen_room_notification_settings_allow_custom">"Permitir configuração personalizada"</string>
|
||||||
|
<string name="screen_room_notification_settings_allow_custom_footnote">"Ativar isso substituirá sua configuração padrão"</string>
|
||||||
|
<string name="screen_room_notification_settings_custom_settings_title">"Me notifique nesta conversa para"</string>
|
||||||
|
<string name="screen_room_notification_settings_default_setting_footnote">"Você pode alterá-lo no seu %1$s."</string>
|
||||||
|
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"configurações globais"</string>
|
||||||
|
<string name="screen_room_notification_settings_default_setting_title">"Configuração padrão"</string>
|
||||||
|
<string name="screen_room_notification_settings_edit_remove_setting">"Remover configuração personalizada"</string>
|
||||||
|
<string name="screen_room_notification_settings_error_loading_settings">"Ocorreu um erro ao carregar as configurações de notificação."</string>
|
||||||
|
<string name="screen_room_notification_settings_error_restoring_default">"Falha ao restaurar o modo padrão, tente novamente."</string>
|
||||||
|
<string name="screen_room_notification_settings_error_setting_mode">"Falha ao definir o modo, tente novamente."</string>
|
||||||
|
<string name="screen_room_notification_settings_mode_all_messages">"Todas as mensagens"</string>
|
||||||
|
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Somente menções e palavras-chave"</string>
|
||||||
|
<string name="screen_room_notification_settings_room_custom_settings_title">"Nesta sala, notifique-me para"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -17,11 +17,15 @@
|
||||||
package io.element.android.features.roomdetails
|
package io.element.android.features.roomdetails
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
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.room.RoomMember
|
||||||
|
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
fun aMatrixRoom(
|
fun aMatrixRoom(
|
||||||
roomId: RoomId = A_ROOM_ID,
|
roomId: RoomId = A_ROOM_ID,
|
||||||
|
|
@ -34,6 +38,16 @@ fun aMatrixRoom(
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||||
emitRoomInfo: Boolean = false,
|
emitRoomInfo: Boolean = false,
|
||||||
|
canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||||
|
canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
|
||||||
|
userDisplayNameResult: () -> Result<String?> = { lambdaError() },
|
||||||
|
userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
|
||||||
|
setNameResult: (String) -> Result<Unit> = { lambdaError() },
|
||||||
|
setTopicResult: (String) -> Result<Unit> = { lambdaError() },
|
||||||
|
updateAvatarResult: (String, ByteArray) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||||
|
removeAvatarResult: () -> Result<Unit> = { lambdaError() },
|
||||||
|
canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||||
|
getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { lambdaError() },
|
||||||
) = FakeMatrixRoom(
|
) = FakeMatrixRoom(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
|
|
@ -42,7 +56,17 @@ fun aMatrixRoom(
|
||||||
isEncrypted = isEncrypted,
|
isEncrypted = isEncrypted,
|
||||||
isPublic = isPublic,
|
isPublic = isPublic,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
notificationSettingsService = notificationSettingsService
|
notificationSettingsService = notificationSettingsService,
|
||||||
|
canInviteResult = canInviteResult,
|
||||||
|
canSendStateResult = canSendStateResult,
|
||||||
|
userDisplayNameResult = userDisplayNameResult,
|
||||||
|
userAvatarUrlResult = userAvatarUrlResult,
|
||||||
|
setNameResult = setNameResult,
|
||||||
|
setTopicResult = setTopicResult,
|
||||||
|
updateAvatarResult = updateAvatarResult,
|
||||||
|
removeAvatarResult = removeAvatarResult,
|
||||||
|
canUserJoinCallResult = canUserJoinCallResult,
|
||||||
|
getUpdatedMemberResult = getUpdatedMemberResult,
|
||||||
).apply {
|
).apply {
|
||||||
if (emitRoomInfo) {
|
if (emitRoomInfo) {
|
||||||
givenRoomInfo(
|
givenRoomInfo(
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,9 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.FakeLifecycleOwner
|
import io.element.android.tests.testutils.FakeLifecycleOwner
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
import io.element.android.tests.testutils.lambda.value
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import io.element.android.tests.testutils.withFakeLifecycleOwner
|
import io.element.android.tests.testutils.withFakeLifecycleOwner
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
@ -110,7 +113,11 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state is created from room if roomInfo is null`() = runTest {
|
fun `present - initial state is created from room if roomInfo is null`() = runTest {
|
||||||
val room = aMatrixRoom()
|
val room = aMatrixRoom(
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
@ -128,7 +135,11 @@ class RoomDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state is updated with roomInfo if it exists`() = runTest {
|
fun `present - initial state is updated with roomInfo if it exists`() = runTest {
|
||||||
val roomInfo = aRoomInfo(name = "A room name", topic = "A topic", avatarUrl = "https://matrix.org/avatar.jpg")
|
val roomInfo = aRoomInfo(name = "A room name", topic = "A topic", avatarUrl = "https://matrix.org/avatar.jpg")
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
).apply {
|
||||||
givenRoomInfo(roomInfo)
|
givenRoomInfo(roomInfo)
|
||||||
}
|
}
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
|
|
@ -145,7 +156,12 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state with no room name`() = runTest {
|
fun `present - initial state with no room name`() = runTest {
|
||||||
val room = aMatrixRoom(displayName = "")
|
val room = aMatrixRoom(
|
||||||
|
displayName = "",
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
@ -162,6 +178,16 @@ class RoomDetailsPresenterTest {
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isEncrypted = true,
|
isEncrypted = true,
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
getUpdatedMemberResult = { userId ->
|
||||||
|
when (userId) {
|
||||||
|
A_SESSION_ID -> Result.success(myRoomMember)
|
||||||
|
A_USER_ID_2 -> Result.success(otherRoomMember)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
).apply {
|
).apply {
|
||||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
@ -181,9 +207,11 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when user can invite others to room`() = runTest {
|
fun `present - initial state when user can invite others to room`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenCanInviteResult(Result.success(true))
|
canInviteResult = { Result.success(true) },
|
||||||
}
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
|
val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
|
||||||
presenter.test {
|
presenter.test {
|
||||||
// Initially false
|
// Initially false
|
||||||
|
|
@ -197,9 +225,11 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when user can not invite others to room`() = runTest {
|
fun `present - initial state when user can not invite others to room`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenCanInviteResult(Result.success(false))
|
canInviteResult = { Result.success(false) },
|
||||||
}
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
assertThat(awaitItem().canInvite).isFalse()
|
assertThat(awaitItem().canInvite).isFalse()
|
||||||
|
|
@ -210,9 +240,11 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when canInvite errors`() = runTest {
|
fun `present - initial state when canInvite errors`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenCanInviteResult(Result.failure(Throwable("Whoops")))
|
canInviteResult = { Result.failure(Throwable("Whoops")) },
|
||||||
}
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
assertThat(awaitItem().canInvite).isFalse()
|
assertThat(awaitItem().canInvite).isFalse()
|
||||||
|
|
@ -223,12 +255,18 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when user can edit one attribute`() = runTest {
|
fun `present - initial state when user can edit one attribute`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
canSendStateResult = { _, stateEventType ->
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
when (stateEventType) {
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp")))
|
StateEventType.ROOM_TOPIC -> Result.success(true)
|
||||||
givenCanInviteResult(Result.success(false))
|
StateEventType.ROOM_NAME -> Result.success(false)
|
||||||
}
|
StateEventType.ROOM_AVATAR -> Result.failure(Throwable("Whelp"))
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = { Result.success(false) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
// Initially false
|
// Initially false
|
||||||
|
|
@ -247,14 +285,26 @@ class RoomDetailsPresenterTest {
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
isEncrypted = true,
|
isEncrypted = true,
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
|
canSendStateResult = { _, stateEventType ->
|
||||||
|
when (stateEventType) {
|
||||||
|
StateEventType.ROOM_TOPIC -> Result.success(true)
|
||||||
|
StateEventType.ROOM_NAME -> Result.success(true)
|
||||||
|
StateEventType.ROOM_AVATAR -> Result.success(true)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = { Result.success(false) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
getUpdatedMemberResult = { userId ->
|
||||||
|
when (userId) {
|
||||||
|
A_SESSION_ID -> Result.success(myRoomMember)
|
||||||
|
A_USER_ID_2 -> Result.success(otherRoomMember)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
).apply {
|
).apply {
|
||||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
|
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
|
|
||||||
givenCanInviteResult(Result.success(false))
|
|
||||||
}
|
}
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
|
@ -278,12 +328,28 @@ class RoomDetailsPresenterTest {
|
||||||
isEncrypted = true,
|
isEncrypted = true,
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
topic = null,
|
topic = null,
|
||||||
|
canSendStateResult = { _, stateEventType ->
|
||||||
|
when (stateEventType) {
|
||||||
|
StateEventType.ROOM_AVATAR,
|
||||||
|
StateEventType.ROOM_TOPIC,
|
||||||
|
StateEventType.ROOM_NAME -> Result.success(true)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
getUpdatedMemberResult = { userId ->
|
||||||
|
when (userId) {
|
||||||
|
A_SESSION_ID -> Result.success(myRoomMember)
|
||||||
|
A_USER_ID_2 -> Result.success(otherRoomMember)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
).apply {
|
).apply {
|
||||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
|
|
@ -297,12 +363,20 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when user can edit all attributes`() = runTest {
|
fun `present - initial state when user can edit all attributes`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
canSendStateResult = { _, stateEventType ->
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
|
when (stateEventType) {
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
|
StateEventType.ROOM_TOPIC -> Result.success(true)
|
||||||
givenCanInviteResult(Result.success(false))
|
StateEventType.ROOM_NAME -> Result.success(true)
|
||||||
}
|
StateEventType.ROOM_AVATAR -> Result.success(true)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = {
|
||||||
|
Result.success(false)
|
||||||
|
},
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
// Initially false
|
// Initially false
|
||||||
|
|
@ -316,12 +390,20 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when user can edit no attributes`() = runTest {
|
fun `present - initial state when user can edit no attributes`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false))
|
canSendStateResult = { _, stateEventType ->
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
when (stateEventType) {
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
|
StateEventType.ROOM_TOPIC -> Result.success(false)
|
||||||
givenCanInviteResult(Result.success(false))
|
StateEventType.ROOM_NAME -> Result.success(false)
|
||||||
}
|
StateEventType.ROOM_AVATAR -> Result.success(false)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = {
|
||||||
|
Result.success(false)
|
||||||
|
},
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
// Initially false, and no further events
|
// Initially false, and no further events
|
||||||
|
|
@ -333,11 +415,21 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - topic state is hidden when no topic and user has no permission`() = runTest {
|
fun `present - topic state is hidden when no topic and user has no permission`() = runTest {
|
||||||
val room = aMatrixRoom(topic = null).apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false))
|
topic = null,
|
||||||
givenCanInviteResult(Result.success(false))
|
canSendStateResult = { _, stateEventType ->
|
||||||
}
|
when (stateEventType) {
|
||||||
|
StateEventType.ROOM_AVATAR,
|
||||||
|
StateEventType.ROOM_NAME -> Result.success(true)
|
||||||
|
StateEventType.ROOM_TOPIC -> Result.success(false)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = {
|
||||||
|
Result.success(false)
|
||||||
|
},
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
// The initial state is "hidden" and no further state changes happen
|
// The initial state is "hidden" and no further state changes happen
|
||||||
|
|
@ -349,12 +441,23 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest {
|
fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest {
|
||||||
val room = aMatrixRoom(topic = null).apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
topic = null,
|
||||||
givenCanInviteResult(Result.success(false))
|
canSendStateResult = { _, stateEventType ->
|
||||||
|
when (stateEventType) {
|
||||||
|
StateEventType.ROOM_AVATAR,
|
||||||
|
StateEventType.ROOM_TOPIC,
|
||||||
|
StateEventType.ROOM_NAME -> Result.success(true)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canInviteResult = {
|
||||||
|
Result.success(false)
|
||||||
|
},
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
).apply {
|
||||||
givenRoomInfo(aRoomInfo(topic = null))
|
givenRoomInfo(aRoomInfo(topic = null))
|
||||||
}
|
}
|
||||||
|
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
// Ignore the initial state
|
// Ignore the initial state
|
||||||
|
|
@ -370,7 +473,11 @@ class RoomDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - leave room event is passed on to leave room presenter`() = runTest {
|
fun `present - leave room event is passed on to leave room presenter`() = runTest {
|
||||||
val leaveRoomPresenter = FakeLeaveRoomPresenter()
|
val leaveRoomPresenter = FakeLeaveRoomPresenter()
|
||||||
val room = aMatrixRoom()
|
val room = aMatrixRoom(
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(
|
val presenter = createRoomDetailsPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
leaveRoomPresenter = leaveRoomPresenter,
|
leaveRoomPresenter = leaveRoomPresenter,
|
||||||
|
|
@ -379,7 +486,11 @@ class RoomDetailsPresenterTest {
|
||||||
presenter.test {
|
presenter.test {
|
||||||
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
|
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
|
||||||
|
|
||||||
assertThat(leaveRoomPresenter.events).contains(LeaveRoomEvent.ShowConfirmation(room.roomId))
|
assertThat(leaveRoomPresenter.events).contains(
|
||||||
|
LeaveRoomEvent.ShowConfirmation(
|
||||||
|
room.roomId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
|
|
@ -389,33 +500,54 @@ class RoomDetailsPresenterTest {
|
||||||
fun `present - notification mode changes`() = runTest {
|
fun `present - notification mode changes`() = runTest {
|
||||||
val leaveRoomPresenter = FakeLeaveRoomPresenter()
|
val leaveRoomPresenter = FakeLeaveRoomPresenter()
|
||||||
val notificationSettingsService = FakeNotificationSettingsService()
|
val notificationSettingsService = FakeNotificationSettingsService()
|
||||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
val room = aMatrixRoom(
|
||||||
|
notificationSettingsService = notificationSettingsService,
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(
|
val presenter = createRoomDetailsPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
leaveRoomPresenter = leaveRoomPresenter,
|
leaveRoomPresenter = leaveRoomPresenter,
|
||||||
notificationSettingsService = notificationSettingsService,
|
notificationSettingsService = notificationSettingsService,
|
||||||
)
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
notificationSettingsService.setRoomNotificationMode(
|
||||||
|
room.roomId,
|
||||||
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||||
|
)
|
||||||
val updatedState = consumeItemsUntilPredicate {
|
val updatedState = consumeItemsUntilPredicate {
|
||||||
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||||
}.last()
|
}.last()
|
||||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
|
||||||
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||||
|
)
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - mute room notifications`() = runTest {
|
fun `present - mute room notifications`() = runTest {
|
||||||
val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
val notificationSettingsService =
|
||||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||||
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService)
|
val room = aMatrixRoom(
|
||||||
|
notificationSettingsService = notificationSettingsService,
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
|
val presenter = createRoomDetailsPresenter(
|
||||||
|
room = room,
|
||||||
|
notificationSettingsService = notificationSettingsService
|
||||||
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
awaitItem().eventSink(RoomDetailsEvent.MuteNotification)
|
awaitItem().eventSink(RoomDetailsEvent.MuteNotification)
|
||||||
val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) {
|
val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) {
|
||||||
it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE
|
it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE
|
||||||
}.last()
|
}.last()
|
||||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MUTE)
|
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
|
||||||
|
RoomNotificationMode.MUTE
|
||||||
|
)
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -426,29 +558,50 @@ class RoomDetailsPresenterTest {
|
||||||
initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||||
initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
|
initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
|
||||||
)
|
)
|
||||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
val room = aMatrixRoom(
|
||||||
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService)
|
notificationSettingsService = notificationSettingsService,
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
|
val presenter = createRoomDetailsPresenter(
|
||||||
|
room = room,
|
||||||
|
notificationSettingsService = notificationSettingsService
|
||||||
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification)
|
awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification)
|
||||||
val updatedState = consumeItemsUntilPredicate {
|
val updatedState = consumeItemsUntilPredicate {
|
||||||
it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES
|
it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES
|
||||||
}.last()
|
}.last()
|
||||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
|
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
|
||||||
|
RoomNotificationMode.ALL_MESSAGES
|
||||||
|
)
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
val setIsFavoriteResult = lambdaRecorder<Boolean, Result<Unit>> { _ -> Result.success(Unit) }
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
setIsFavoriteResult = setIsFavoriteResult,
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val presenter = createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
|
val presenter =
|
||||||
|
createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEvent.SetFavorite(true))
|
initialState.eventSink(RoomDetailsEvent.SetFavorite(true))
|
||||||
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true))
|
setIsFavoriteResult.assertions().isCalledOnce().with(value(true))
|
||||||
initialState.eventSink(RoomDetailsEvent.SetFavorite(false))
|
initialState.eventSink(RoomDetailsEvent.SetFavorite(false))
|
||||||
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false))
|
setIsFavoriteResult.assertions().isCalledExactly(2)
|
||||||
|
.withSequence(
|
||||||
|
listOf(value(true)),
|
||||||
|
listOf(value(false)),
|
||||||
|
)
|
||||||
assertThat(analyticsService.capturedEvents).containsExactly(
|
assertThat(analyticsService.capturedEvents).containsExactly(
|
||||||
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle),
|
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle),
|
||||||
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle)
|
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle)
|
||||||
|
|
@ -459,7 +612,11 @@ class RoomDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - changes in room info updates the is favorite flag`() = runTest {
|
fun `present - changes in room info updates the is favorite flag`() = runTest {
|
||||||
val room = aMatrixRoom()
|
val room = aMatrixRoom(
|
||||||
|
canInviteResult = { Result.success(true) },
|
||||||
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsPresenter(room = room)
|
val presenter = createRoomDetailsPresenter(room = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
room.givenRoomInfo(aRoomInfo(isFavorite = true))
|
room.givenRoomInfo(aRoomInfo(isFavorite = true))
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
import io.element.android.tests.testutils.lambda.value
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
|
|
@ -98,6 +101,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = A_ROOM_NAME,
|
displayName = A_ROOM_NAME,
|
||||||
rawName = A_ROOM_RAW_NAME,
|
rawName = A_ROOM_RAW_NAME,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -120,11 +124,17 @@ class RoomDetailsEditPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - sets canChangeName if user has permission`() = runTest {
|
fun `present - sets canChangeName if user has permission`() = runTest {
|
||||||
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
|
avatarUrl = AN_AVATAR_URL,
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
|
canSendStateResult = { _, stateEventType ->
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
|
when (stateEventType) {
|
||||||
}
|
StateEventType.ROOM_NAME -> Result.success(true)
|
||||||
|
StateEventType.ROOM_AVATAR -> Result.success(false)
|
||||||
|
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops"))
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -144,11 +154,17 @@ class RoomDetailsEditPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - sets canChangeAvatar if user has permission`() = runTest {
|
fun `present - sets canChangeAvatar if user has permission`() = runTest {
|
||||||
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
avatarUrl = AN_AVATAR_URL,
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
|
canSendStateResult = { _, stateEventType ->
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
|
when (stateEventType) {
|
||||||
}
|
StateEventType.ROOM_NAME -> Result.success(false)
|
||||||
|
StateEventType.ROOM_AVATAR -> Result.success(true)
|
||||||
|
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops"))
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -168,11 +184,17 @@ class RoomDetailsEditPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - sets canChangeTopic if user has permission`() = runTest {
|
fun `present - sets canChangeTopic if user has permission`() = runTest {
|
||||||
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
|
val room = aMatrixRoom(
|
||||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
avatarUrl = AN_AVATAR_URL,
|
||||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Oops")))
|
canSendStateResult = { _, stateEventType ->
|
||||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
when (stateEventType) {
|
||||||
}
|
StateEventType.ROOM_NAME -> Result.success(false)
|
||||||
|
StateEventType.ROOM_AVATAR -> Result.failure(Throwable("Oops"))
|
||||||
|
StateEventType.ROOM_TOPIC -> Result.success(true)
|
||||||
|
else -> lambdaError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -197,6 +219,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -240,6 +263,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
|
|
@ -262,6 +286,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
val fakePermissionsPresenter = FakePermissionsPresenter()
|
val fakePermissionsPresenter = FakePermissionsPresenter()
|
||||||
|
|
@ -298,6 +323,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(roomAvatarUri)
|
fakePickerProvider.givenResult(roomAvatarUri)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
|
|
@ -346,6 +372,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "fallback",
|
displayName = "fallback",
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
fakePickerProvider.givenResult(roomAvatarUri)
|
fakePickerProvider.givenResult(roomAvatarUri)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
|
|
@ -389,11 +416,18 @@ class RoomDetailsEditPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save changes room details if different`() = runTest {
|
fun `present - save changes room details if different`() = runTest {
|
||||||
|
val setNameResult = lambdaRecorder { _: String -> Result.success(Unit) }
|
||||||
|
val setTopicResult = lambdaRecorder { _: String -> Result.success(Unit) }
|
||||||
|
val removeAvatarResult = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||||
val room = aMatrixRoom(
|
val room = aMatrixRoom(
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
|
setNameResult = setNameResult,
|
||||||
|
setTopicResult = setTopicResult,
|
||||||
|
removeAvatarResult = removeAvatarResult,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
)
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -405,16 +439,20 @@ class RoomDetailsEditPresenterTest {
|
||||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
|
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
skipItems(5)
|
skipItems(5)
|
||||||
assertThat(room.newName).isEqualTo("New name")
|
setNameResult.assertions().isCalledOnce().with(value("New name"))
|
||||||
assertThat(room.newTopic).isEqualTo("New topic")
|
setTopicResult.assertions().isCalledOnce().with(value("New topic"))
|
||||||
assertThat(room.newAvatarData).isNull()
|
removeAvatarResult.assertions().isCalledOnce()
|
||||||
assertThat(room.removedAvatar).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save doesn't change room details if they're the same trimmed`() = runTest {
|
fun `present - save doesn't change room details if they're the same trimmed`() = runTest {
|
||||||
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val room = aMatrixRoom(
|
||||||
|
topic = "My topic",
|
||||||
|
displayName = "Name",
|
||||||
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -423,17 +461,18 @@ class RoomDetailsEditPresenterTest {
|
||||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name "))
|
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name "))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic "))
|
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic "))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
assertThat(room.newName).isNull()
|
|
||||||
assertThat(room.newTopic).isNull()
|
|
||||||
assertThat(room.newAvatarData).isNull()
|
|
||||||
assertThat(room.removedAvatar).isFalse()
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save doesn't change topic if it was unset and is now blank`() = runTest {
|
fun `present - save doesn't change topic if it was unset and is now blank`() = runTest {
|
||||||
val room = aMatrixRoom(topic = null, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val room = aMatrixRoom(
|
||||||
|
topic = null,
|
||||||
|
displayName = "Name",
|
||||||
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -441,17 +480,18 @@ class RoomDetailsEditPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(""))
|
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(""))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
assertThat(room.newName).isNull()
|
|
||||||
assertThat(room.newTopic).isNull()
|
|
||||||
assertThat(room.newAvatarData).isNull()
|
|
||||||
assertThat(room.removedAvatar).isFalse()
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save doesn't change name if it's now empty`() = runTest {
|
fun `present - save doesn't change name if it's now empty`() = runTest {
|
||||||
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val room = aMatrixRoom(
|
||||||
|
topic = "My topic",
|
||||||
|
displayName = "Name",
|
||||||
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -459,17 +499,20 @@ class RoomDetailsEditPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(""))
|
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(""))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
assertThat(room.newName).isNull()
|
|
||||||
assertThat(room.newTopic).isNull()
|
|
||||||
assertThat(room.newAvatarData).isNull()
|
|
||||||
assertThat(room.removedAvatar).isFalse()
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
|
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
|
||||||
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val updateAvatarResult = lambdaRecorder { _: String, _: ByteArray -> Result.success(Unit) }
|
||||||
|
val room = aMatrixRoom(
|
||||||
|
topic = "My topic",
|
||||||
|
displayName = "Name",
|
||||||
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
updateAvatarResult = updateAvatarResult,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
|
)
|
||||||
givenPickerReturnsFile()
|
givenPickerReturnsFile()
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -478,17 +521,19 @@ class RoomDetailsEditPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
skipItems(3)
|
skipItems(4)
|
||||||
assertThat(room.newName).isNull()
|
updateAvatarResult.assertions().isCalledOnce().with(value("image/jpeg"), value(fakeFileContents))
|
||||||
assertThat(room.newTopic).isNull()
|
|
||||||
assertThat(room.newAvatarData).isSameInstanceAs(fakeFileContents)
|
|
||||||
assertThat(room.removedAvatar).isFalse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - save does not set avatar data if processor fails`() = runTest {
|
fun `present - save does not set avatar data if processor fails`() = runTest {
|
||||||
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val room = aMatrixRoom(
|
||||||
|
topic = "My topic",
|
||||||
|
displayName = "Name",
|
||||||
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
|
)
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
|
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
|
|
@ -498,11 +543,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
skipItems(2)
|
skipItems(3)
|
||||||
assertThat(room.newName).isNull()
|
|
||||||
assertThat(room.newTopic).isNull()
|
|
||||||
assertThat(room.newAvatarData).isNull()
|
|
||||||
assertThat(room.removedAvatar).isFalse()
|
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -514,9 +555,9 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
).apply {
|
setNameResult = { Result.failure(Throwable("!")) },
|
||||||
givenSetNameResult(Result.failure(Throwable("!")))
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
}
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
|
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -527,9 +568,9 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
).apply {
|
setTopicResult = { Result.failure(Throwable("!")) },
|
||||||
givenSetTopicResult(Result.failure(Throwable("!")))
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
}
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
|
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,9 +581,9 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
).apply {
|
removeAvatarResult = { Result.failure(Throwable("!")) },
|
||||||
givenRemoveAvatarResult(Result.failure(Throwable("!")))
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
}
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
|
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -554,18 +595,22 @@ class RoomDetailsEditPresenterTest {
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
emitRoomInfo = true,
|
emitRoomInfo = true,
|
||||||
).apply {
|
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
|
||||||
givenUpdateAvatarResult(Result.failure(Throwable("!")))
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
}
|
)
|
||||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
||||||
givenPickerReturnsFile()
|
givenPickerReturnsFile()
|
||||||
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply {
|
val room = aMatrixRoom(
|
||||||
givenSetTopicResult(Result.failure(Throwable("!")))
|
topic = "My topic",
|
||||||
}
|
displayName = "Name",
|
||||||
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
setTopicResult = { Result.failure(Throwable("!")) },
|
||||||
|
canSendStateResult = { _, _ -> Result.success(true) }
|
||||||
|
)
|
||||||
val presenter = createRoomDetailsEditPresenter(room)
|
val presenter = createRoomDetailsEditPresenter(room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -573,7 +618,7 @@ class RoomDetailsEditPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo"))
|
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo"))
|
||||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||||
skipItems(2)
|
skipItems(3)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges)
|
initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,10 @@ class RoomMemberListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `member loading is done automatically on start, but is async`() = runTest {
|
fun `member loading is done automatically on start, but is async`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
canInviteResult = { Result.success(true) }
|
||||||
|
).apply {
|
||||||
// Needed to avoid discarding the loaded members as a partial and invalid result
|
// Needed to avoid discarding the loaded members as a partial and invalid result
|
||||||
givenRoomInfo(aRoomInfo(joinedMembersCount = 2))
|
givenRoomInfo(aRoomInfo(joinedMembersCount = 2))
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +81,12 @@ class RoomMemberListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `open search`() = runTest {
|
fun `open search`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter(
|
||||||
|
matrixRoom = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
canInviteResult = { Result.success(true) }
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -93,7 +101,12 @@ class RoomMemberListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `search for something which is not found`() = runTest {
|
fun `search for something which is not found`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter(
|
||||||
|
matrixRoom = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
canInviteResult = { Result.success(true) }
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -112,7 +125,12 @@ class RoomMemberListPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `search for something which is found`() = runTest {
|
fun `search for something which is found`() = runTest {
|
||||||
val presenter = createPresenter()
|
val presenter = createPresenter(
|
||||||
|
matrixRoom = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
canInviteResult = { Result.success(true) }
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -134,9 +152,10 @@ class RoomMemberListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - asynchronously sets canInvite when user has correct power level`() = runTest {
|
fun `present - asynchronously sets canInvite when user has correct power level`() = runTest {
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
matrixRoom = FakeMatrixRoom().apply {
|
matrixRoom = FakeMatrixRoom(
|
||||||
givenCanInviteResult(Result.success(true))
|
canInviteResult = { Result.success(true) },
|
||||||
}
|
updateMembersResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -150,9 +169,10 @@ class RoomMemberListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - asynchronously sets canInvite when user does not have correct power level`() = runTest {
|
fun `present - asynchronously sets canInvite when user does not have correct power level`() = runTest {
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
matrixRoom = FakeMatrixRoom().apply {
|
matrixRoom = FakeMatrixRoom(
|
||||||
givenCanInviteResult(Result.success(false))
|
canInviteResult = { Result.success(false) },
|
||||||
}
|
updateMembersResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -166,9 +186,10 @@ class RoomMemberListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - asynchronously sets canInvite when power level check fails`() = runTest {
|
fun `present - asynchronously sets canInvite when power level check fails`() = runTest {
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
matrixRoom = FakeMatrixRoom().apply {
|
matrixRoom = FakeMatrixRoom(
|
||||||
givenCanInviteResult(Result.failure(Throwable("Eek")))
|
canInviteResult = { Result.failure(Throwable("Eek")) },
|
||||||
}
|
updateMembersResult = { Result.success(Unit) }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -183,7 +204,14 @@ class RoomMemberListPresenterTest {
|
||||||
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
|
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
|
||||||
val navigator = FakeRoomMemberListNavigator()
|
val navigator = FakeRoomMemberListNavigator()
|
||||||
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = false)
|
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = false)
|
||||||
val presenter = createPresenter(moderationPresenter = moderationPresenter, navigator = navigator)
|
val presenter = createPresenter(
|
||||||
|
moderationPresenter = moderationPresenter,
|
||||||
|
navigator = navigator,
|
||||||
|
matrixRoom = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
canInviteResult = { Result.success(true) }
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -205,7 +233,14 @@ class RoomMemberListPresenterTest {
|
||||||
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply {
|
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply {
|
||||||
givenState(capturingState)
|
givenState(capturingState)
|
||||||
}
|
}
|
||||||
val presenter = createPresenter(moderationPresenter = moderationPresenter, navigator = navigator)
|
val presenter = createPresenter(
|
||||||
|
moderationPresenter = moderationPresenter,
|
||||||
|
navigator = navigator,
|
||||||
|
matrixRoom = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
canInviteResult = { Result.success(true) }
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -236,10 +271,12 @@ private fun TestScope.createDataSource(
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
private fun TestScope.createPresenter(
|
private fun TestScope.createPresenter(
|
||||||
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||||
matrixRoom: MatrixRoom = FakeMatrixRoom(),
|
matrixRoom: MatrixRoom = FakeMatrixRoom(
|
||||||
|
updateMembersResult = { Result.success(Unit) }
|
||||||
|
),
|
||||||
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
|
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
|
||||||
moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(),
|
moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(),
|
||||||
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator { }
|
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
|
||||||
) = RoomMemberListPresenter(
|
) = RoomMemberListPresenter(
|
||||||
room = matrixRoom,
|
room = matrixRoom,
|
||||||
roomMemberListDataSource = roomMemberListDataSource,
|
roomMemberListDataSource = roomMemberListDataSource,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
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.MatrixRoomMembersState
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
|
@ -53,9 +54,11 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - returns the room member's data, then updates it if needed`() = runTest {
|
fun `present - returns the room member's data, then updates it if needed`() = runTest {
|
||||||
val roomMember = aRoomMember(displayName = "Alice")
|
val roomMember = aRoomMember(displayName = "Alice")
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenUserDisplayNameResult(Result.success("A custom name"))
|
userDisplayNameResult = { Result.success("A custom name") },
|
||||||
givenUserAvatarUrlResult(Result.success("A custom avatar"))
|
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||||
|
getUpdatedMemberResult = { Result.success(roomMember) },
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
||||||
}
|
}
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
|
@ -82,11 +85,14 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - will recover when retrieving room member details fails`() = runTest {
|
fun `present - will recover when retrieving room member details fails`() = runTest {
|
||||||
val roomMember = aRoomMember(displayName = "Alice")
|
val roomMember = aRoomMember(displayName = "Alice")
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenUserDisplayNameResult(Result.failure(Throwable()))
|
userDisplayNameResult = { Result.failure(Throwable()) },
|
||||||
givenUserAvatarUrlResult(Result.failure(Throwable()))
|
userAvatarUrlResult = { Result.failure(Throwable()) },
|
||||||
|
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
||||||
}
|
}
|
||||||
|
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
roomMemberId = roomMember.userId
|
roomMemberId = roomMember.userId
|
||||||
|
|
@ -105,9 +111,11 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - will fallback to original data if the updated data is null`() = runTest {
|
fun `present - will fallback to original data if the updated data is null`() = runTest {
|
||||||
val roomMember = aRoomMember(displayName = "Alice")
|
val roomMember = aRoomMember(displayName = "Alice")
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenUserDisplayNameResult(Result.success(null))
|
userDisplayNameResult = { Result.success(null) },
|
||||||
givenUserAvatarUrlResult(Result.success(null))
|
userAvatarUrlResult = { Result.success(null) },
|
||||||
|
getUpdatedMemberResult = { Result.success(roomMember) }
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
||||||
}
|
}
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
|
@ -128,10 +136,11 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - will fallback to user profile if user is not a member of the room`() = runTest {
|
fun `present - will fallback to user profile if user is not a member of the room`() = runTest {
|
||||||
val bobProfile = aMatrixUser("@bob:server.org", "Bob", avatarUrl = "anAvatarUrl")
|
val bobProfile = aMatrixUser("@bob:server.org", "Bob", avatarUrl = "anAvatarUrl")
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom(
|
||||||
givenUserDisplayNameResult(Result.failure(Exception("Not a member!")))
|
userDisplayNameResult = { Result.failure(Exception("Not a member!")) },
|
||||||
givenUserAvatarUrlResult(Result.failure(Exception("Not a member!")))
|
userAvatarUrlResult = { Result.failure(Exception("Not a member!")) },
|
||||||
}
|
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||||
|
)
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetProfileResult(bobProfile.userId, Result.success(bobProfile))
|
givenGetProfileResult(bobProfile.userId, Result.success(bobProfile))
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +163,13 @@ class RoomMemberDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest {
|
fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest {
|
||||||
val presenter = createRoomMemberDetailsPresenter()
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
room = aMatrixRoom(
|
||||||
|
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||||
|
userDisplayNameResult = { Result.success("Alice") },
|
||||||
|
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -176,6 +191,11 @@ class RoomMemberDetailsPresenterTest {
|
||||||
val client = FakeMatrixClient()
|
val client = FakeMatrixClient()
|
||||||
val roomMember = aRoomMember()
|
val roomMember = aRoomMember()
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
room = aMatrixRoom(
|
||||||
|
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||||
|
userDisplayNameResult = { Result.success("Alice") },
|
||||||
|
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||||
|
),
|
||||||
client = client,
|
client = client,
|
||||||
roomMemberId = roomMember.userId
|
roomMemberId = roomMember.userId
|
||||||
)
|
)
|
||||||
|
|
@ -199,13 +219,21 @@ class RoomMemberDetailsPresenterTest {
|
||||||
fun `present - BlockUser with error`() = runTest {
|
fun `present - BlockUser with error`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE))
|
matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE))
|
||||||
val presenter = createRoomMemberDetailsPresenter(client = matrixClient)
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
client = matrixClient,
|
||||||
|
room = aMatrixRoom(
|
||||||
|
getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) },
|
||||||
|
userDisplayNameResult = { Result.success("Alice") },
|
||||||
|
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitFirstItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
|
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
|
skipItems(2)
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
||||||
// Clear error
|
// Clear error
|
||||||
|
|
@ -218,13 +246,21 @@ class RoomMemberDetailsPresenterTest {
|
||||||
fun `present - UnblockUser with error`() = runTest {
|
fun `present - UnblockUser with error`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE))
|
matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE))
|
||||||
val presenter = createRoomMemberDetailsPresenter(client = matrixClient)
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
room = aMatrixRoom(
|
||||||
|
getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) },
|
||||||
|
userDisplayNameResult = { Result.success("Alice") },
|
||||||
|
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||||
|
),
|
||||||
|
client = matrixClient,
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitFirstItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
|
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
|
skipItems(2)
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
||||||
// Clear error
|
// Clear error
|
||||||
|
|
@ -235,7 +271,13 @@ class RoomMemberDetailsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest {
|
fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest {
|
||||||
val presenter = createRoomMemberDetailsPresenter()
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
room = aMatrixRoom(
|
||||||
|
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||||
|
userDisplayNameResult = { Result.success("Alice") },
|
||||||
|
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -255,7 +297,14 @@ class RoomMemberDetailsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - start DM action complete scenario`() = runTest {
|
fun `present - start DM action complete scenario`() = runTest {
|
||||||
val startDMAction = FakeStartDMAction()
|
val startDMAction = FakeStartDMAction()
|
||||||
val presenter = createRoomMemberDetailsPresenter(startDMAction = startDMAction)
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
room = aMatrixRoom(
|
||||||
|
getUpdatedMemberResult = { Result.success(aRoomMember(displayName = "Alice")) },
|
||||||
|
userDisplayNameResult = { Result.success("Alice") },
|
||||||
|
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||||
|
),
|
||||||
|
startDMAction = startDMAction,
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -268,6 +317,7 @@ class RoomMemberDetailsPresenterTest {
|
||||||
startDMAction.givenExecuteResult(startDMFailureResult)
|
startDMAction.givenExecuteResult(startDMFailureResult)
|
||||||
initialState.eventSink(UserProfileEvents.StartDM)
|
initialState.eventSink(UserProfileEvents.StartDM)
|
||||||
assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java)
|
assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
|
skipItems(2)
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.startDmActionState).isEqualTo(startDMFailureResult)
|
assertThat(state.startDmActionState).isEqualTo(startDMFailureResult)
|
||||||
state.eventSink(UserProfileEvents.ClearStartDMState)
|
state.eventSink(UserProfileEvents.ClearStartDMState)
|
||||||
|
|
@ -292,8 +342,8 @@ class RoomMemberDetailsPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRoomMemberDetailsPresenter(
|
private fun createRoomMemberDetailsPresenter(
|
||||||
|
room: MatrixRoom,
|
||||||
client: MatrixClient = FakeMatrixClient(),
|
client: MatrixClient = FakeMatrixClient(),
|
||||||
room: MatrixRoom = aMatrixRoom(),
|
|
||||||
roomMemberId: UserId = UserId("@alice:server.org"),
|
roomMemberId: UserId = UserId("@alice:server.org"),
|
||||||
startDMAction: StartDMAction = FakeStartDMAction()
|
startDMAction: StartDMAction = FakeStartDMAction()
|
||||||
): RoomMemberDetailsPresenter {
|
): RoomMemberDetailsPresenter {
|
||||||
|
|
|
||||||
|
|
@ -54,29 +54,34 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
|
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||||
val room = FakeMatrixRoom(isDirect = false, activeMemberCount = 10).apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
isDirect = false,
|
||||||
}
|
activeMemberCount = 10,
|
||||||
|
canKickResult = { Result.success(true) },
|
||||||
|
canBanResult = { Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
|
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
|
||||||
val room = FakeMatrixRoom(isDirect = false, activeMemberCount = 10).apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanBanResult(Result.success(true))
|
isDirect = false,
|
||||||
}
|
activeMemberCount = 10,
|
||||||
|
canBanResult = { Result.success(true) },
|
||||||
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
}
|
)
|
||||||
val selectedMember = aVictor()
|
val selectedMember = aVictor()
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -98,11 +103,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest {
|
fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest {
|
||||||
val room = FakeMatrixRoom(sessionId = A_USER_ID).apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
sessionId = A_USER_ID,
|
||||||
givenCanBanResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
canBanResult = { Result.success(true) },
|
||||||
}
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
)
|
||||||
val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L)
|
val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -123,11 +129,11 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest {
|
fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest {
|
||||||
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
}
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -144,11 +150,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - Kick removes the user`() = runTest {
|
fun `present - Kick removes the user`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
}
|
kickUserResult = { _, _ -> Result.success(Unit) },
|
||||||
|
)
|
||||||
val selectedMember = aVictor()
|
val selectedMember = aVictor()
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -171,11 +178,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
}
|
banUserResult = { _, _ -> Result.success(Unit) },
|
||||||
|
)
|
||||||
val selectedMember = aVictor()
|
val selectedMember = aVictor()
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -204,11 +212,13 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
|
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
|
unBanUserResult = { _, _ -> Result.success(Unit) },
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember)))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember)))
|
||||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
|
||||||
}
|
}
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -231,10 +241,11 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Reset removes the selected user and actions`() = runTest {
|
fun `present - Reset removes the selected user and actions`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
}
|
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||||
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -251,13 +262,14 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Reset resets any async actions`() = runTest {
|
fun `present - Reset resets any async actions`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenCanKickResult(Result.success(true))
|
canKickResult = { Result.success(true) },
|
||||||
givenCanBanResult(Result.success(true))
|
canBanResult = { Result.success(true) },
|
||||||
givenKickUserResult(Result.failure(Throwable("Eek")))
|
kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||||
givenBanUserResult(Result.failure(Throwable("Eek")))
|
banUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||||
givenUnbanUserResult(Result.failure(Throwable("Eek")))
|
unBanUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||||
}
|
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||||
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.defaultRoomPowerLevels
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -67,7 +68,12 @@ class RolesAndPermissionPresenterTest {
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Test
|
@Test
|
||||||
fun `present - DemoteSelfTo changes own role to the specified one`() = runTest(StandardTestDispatcher()) {
|
fun `present - DemoteSelfTo changes own role to the specified one`() = runTest(StandardTestDispatcher()) {
|
||||||
val presenter = createRolesAndPermissionsPresenter(dispatchers = testCoroutineDispatchers())
|
val presenter = createRolesAndPermissionsPresenter(
|
||||||
|
dispatchers = testCoroutineDispatchers(),
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
updateUserRoleResult = { Result.success(Unit) }
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -85,9 +91,9 @@ class RolesAndPermissionPresenterTest {
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Test
|
@Test
|
||||||
fun `present - DemoteSelfTo can handle failures and clean them`() = runTest(StandardTestDispatcher()) {
|
fun `present - DemoteSelfTo can handle failures and clean them`() = runTest(StandardTestDispatcher()) {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenUpdateUserRoleResult(Result.failure(Exception("Failed to update role")))
|
updateUserRoleResult = { Result.failure(Exception("Failed to update role")) }
|
||||||
}
|
)
|
||||||
val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers())
|
val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers())
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -123,7 +129,12 @@ class RolesAndPermissionPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - ResetPermissions needs confirmation, then resets permissions`() = runTest {
|
fun `present - ResetPermissions needs confirmation, then resets permissions`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val presenter = createRolesAndPermissionsPresenter(analyticsService = analyticsService)
|
val presenter = createRolesAndPermissionsPresenter(
|
||||||
|
analyticsService = analyticsService,
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
resetPowerLevelsResult = { Result.success(defaultRoomPowerLevels()) }
|
||||||
|
)
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,10 @@ class ChangeRolesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Save will display a confirmation when adding admins`() = runTest {
|
fun `present - Save will display a confirmation when adding admins`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
|
updateUserRoleResult = { Result.success(Unit) },
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
||||||
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 100)))
|
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 100)))
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +328,10 @@ class ChangeRolesPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - Save will just save the data for moderators`() = runTest {
|
fun `present - Save will just save the data for moderators`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
|
updateUserRoleResult = { Result.success(Unit) },
|
||||||
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
||||||
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
|
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
|
||||||
}
|
}
|
||||||
|
|
@ -351,10 +357,11 @@ class ChangeRolesPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Save can handle failures and ClearError clears them`() = runTest {
|
fun `present - Save can handle failures and ClearError clears them`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
|
updateUserRoleResult = { Result.failure(IllegalStateException("Failed")) }
|
||||||
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
||||||
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
|
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
|
||||||
givenUpdateUserRoleResult(Result.failure(IllegalStateException("Failed")))
|
|
||||||
}
|
}
|
||||||
val presenter = createChangeRolesPresenter(role = RoomMember.Role.MODERATOR, room = room)
|
val presenter = createChangeRolesPresenter(role = RoomMember.Role.MODERATOR, room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,13 @@ class ChangeRoomPermissionsPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - Save updates the current permissions and resets hasChanges`() = runTest {
|
fun `present - Save updates the current permissions and resets hasChanges`() = runTest {
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val presenter = createChangeRoomPermissionsPresenter(analyticsService = analyticsService)
|
val presenter = createChangeRoomPermissionsPresenter(
|
||||||
|
analyticsService = analyticsService,
|
||||||
|
room = FakeMatrixRoom(
|
||||||
|
updatePowerLevelsResult = { Result.success(Unit) },
|
||||||
|
powerLevelsResult = { Result.success(defaultPermissions()) }
|
||||||
|
),
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -208,9 +214,9 @@ class ChangeRoomPermissionsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Save will fail if there are not current permissions`() = runTest {
|
fun `present - Save will fail if there are not current permissions`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenPowerLevelsResult(Result.failure(IllegalStateException("Failed to load power levels")))
|
powerLevelsResult = { Result.failure(IllegalStateException("Failed to load power levels")) }
|
||||||
}
|
)
|
||||||
val presenter = createChangeRoomPermissionsPresenter(room = room)
|
val presenter = createChangeRoomPermissionsPresenter(room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -225,9 +231,10 @@ class ChangeRoomPermissionsPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - Save can handle failures and they can be cleared`() = runTest {
|
fun `present - Save can handle failures and they can be cleared`() = runTest {
|
||||||
val room = FakeMatrixRoom().apply {
|
val room = FakeMatrixRoom(
|
||||||
givenUpdatePowerLevelsResult(Result.failure(IllegalStateException("Failed to update power levels")))
|
powerLevelsResult = { Result.success(defaultPermissions()) },
|
||||||
}
|
updatePowerLevelsResult = { Result.failure(IllegalStateException("Failed to update power levels")) },
|
||||||
|
)
|
||||||
val presenter = createChangeRoomPermissionsPresenter(room = room)
|
val presenter = createChangeRoomPermissionsPresenter(room = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -292,7 +299,9 @@ class ChangeRoomPermissionsPresenterTest {
|
||||||
|
|
||||||
private fun createChangeRoomPermissionsPresenter(
|
private fun createChangeRoomPermissionsPresenter(
|
||||||
section: ChangeRoomPermissionsSection = ChangeRoomPermissionsSection.RoomDetails,
|
section: ChangeRoomPermissionsSection = ChangeRoomPermissionsSection.RoomDetails,
|
||||||
room: FakeMatrixRoom = FakeMatrixRoom(),
|
room: FakeMatrixRoom = FakeMatrixRoom(
|
||||||
|
powerLevelsResult = { Result.success(defaultPermissions()) }
|
||||||
|
),
|
||||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||||
) = ChangeRoomPermissionsPresenter(
|
) = ChangeRoomPermissionsPresenter(
|
||||||
section = section,
|
section = section,
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,13 @@
|
||||||
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Μπορείς να καταργήσεις την επιλογή φίλτρων για να δεις τις άλλες συνομιλίες σου"</string>
|
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Μπορείς να καταργήσεις την επιλογή φίλτρων για να δεις τις άλλες συνομιλίες σου"</string>
|
||||||
<string name="screen_roomlist_filter_mixed_empty_state_title">"Δεν έχεις συνομιλίες για αυτήν την επιλογή"</string>
|
<string name="screen_roomlist_filter_mixed_empty_state_title">"Δεν έχεις συνομιλίες για αυτήν την επιλογή"</string>
|
||||||
<string name="screen_roomlist_filter_people">"Άτομα"</string>
|
<string name="screen_roomlist_filter_people">"Άτομα"</string>
|
||||||
|
<string name="screen_roomlist_filter_people_empty_state_title">"Δεν έχεις ακόμα ΠΜ"</string>
|
||||||
<string name="screen_roomlist_filter_rooms">"Δωμάτια"</string>
|
<string name="screen_roomlist_filter_rooms">"Δωμάτια"</string>
|
||||||
<string name="screen_roomlist_filter_rooms_empty_state_title">"Δεν είσαι ακόμα σε κανένα δωμάτιο"</string>
|
<string name="screen_roomlist_filter_rooms_empty_state_title">"Δεν είσαι ακόμα σε κανένα δωμάτιο"</string>
|
||||||
<string name="screen_roomlist_filter_unreads">"Μη αναγνωσμένα"</string>
|
<string name="screen_roomlist_filter_unreads">"Μη αναγνωσμένα"</string>
|
||||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Συγχαρητήρια!
|
<string name="screen_roomlist_filter_unreads_empty_state_title">"Συγχαρητήρια!
|
||||||
Δεν έχεις μη αναγνωσμένα μηνύματα!"</string>
|
Δεν έχεις μη αναγνωσμένα μηνύματα!"</string>
|
||||||
<string name="screen_roomlist_main_space_title">"Συζητήσεις"</string>
|
<string name="screen_roomlist_main_space_title">"Συνομιλίες"</string>
|
||||||
<string name="screen_roomlist_mark_as_read">"Επισήμανση ως αναγνωσμένου"</string>
|
<string name="screen_roomlist_mark_as_read">"Επισήμανση ως αναγνωσμένου"</string>
|
||||||
<string name="screen_roomlist_mark_as_unread">"Επισήμανση ως μη αναγνωσμένου"</string>
|
<string name="screen_roomlist_mark_as_unread">"Επισήμανση ως μη αναγνωσμένου"</string>
|
||||||
<string name="screen_roomlist_room_directory_button_title">"Περιήγηση σε όλα τα δωμάτια"</string>
|
<string name="screen_roomlist_room_directory_button_title">"Περιήγηση σε όλα τα δωμάτια"</string>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="confirm_recovery_key_banner_message">"თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე."</string>
|
||||||
|
<string name="confirm_recovery_key_banner_title">"შეიყვანეთ აღდგენის გასაღები"</string>
|
||||||
<string name="screen_invites_decline_chat_message">"დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ მოწვევაზე %1$s-ში?"</string>
|
<string name="screen_invites_decline_chat_message">"დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ მოწვევაზე %1$s-ში?"</string>
|
||||||
<string name="screen_invites_decline_chat_title">"მოწვევაზე უარის თქმა"</string>
|
<string name="screen_invites_decline_chat_title">"მოწვევაზე უარის თქმა"</string>
|
||||||
<string name="screen_invites_decline_direct_chat_message">"დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ ჩატზე %1$s-თან?"</string>
|
<string name="screen_invites_decline_direct_chat_message">"დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ ჩატზე %1$s-თან?"</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="confirm_recovery_key_banner_message">"Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania."</string>
|
||||||
|
<string name="confirm_recovery_key_banner_title">"Potwierdź klucz odzyskiwania"</string>
|
||||||
|
<string name="full_screen_intent_banner_message">"Upewnij się, że nie pominiesz żadnego połączenia. Zmień swoje ustawienia i zezwól na powiadomienia na blokadzie ekranu."</string>
|
||||||
|
<string name="full_screen_intent_banner_title">"Popraw jakość swoich rozmów"</string>
|
||||||
|
<string name="screen_invites_decline_chat_message">"Czy na pewno chcesz odrzucić zaproszenie do dołączenia do %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_chat_title">"Odrzuć zaproszenie"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_message">"Czy na pewno chcesz odrzucić rozmowę prywatną z %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_title">"Odrzuć czat"</string>
|
||||||
|
<string name="screen_invites_empty_list">"Brak zaproszeń"</string>
|
||||||
|
<string name="screen_invites_invited_you">"%1$s (%2$s) zaprosił Cię"</string>
|
||||||
|
<string name="screen_migration_message">"Jest to jednorazowy proces, dziękujemy za czekanie."</string>
|
||||||
|
<string name="screen_migration_title">"Konfigurowanie Twojego konta."</string>
|
||||||
|
<string name="screen_roomlist_a11y_create_message">"Utwórz nową rozmowę lub pokój"</string>
|
||||||
|
<string name="screen_roomlist_empty_message">"Wyślij komuś wiadomość, aby rozpocząć."</string>
|
||||||
|
<string name="screen_roomlist_empty_title">"Brak czatów."</string>
|
||||||
|
<string name="screen_roomlist_filter_people">"Osoby"</string>
|
||||||
|
<string name="screen_roomlist_main_space_title">"Wszystkie czaty"</string>
|
||||||
|
<string name="session_verification_banner_message">"Wygląda na to, że używasz nowego urządzenia. Zweryfikuj się innym urządzeniem, aby uzyskać dostęp do zaszyfrowanych wiadomości."</string>
|
||||||
|
<string name="session_verification_banner_title">"Potwierdź, że to Ty"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="confirm_recovery_key_banner_title">"Insira sua chave de recuperação"</string>
|
||||||
|
<string name="screen_invites_decline_chat_message">"Tem certeza de que deseja recusar o convite para ingressar em %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_chat_title">"Recusar convite"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_message">"Tem certeza de que deseja recusar esse chat privado com %1$s?"</string>
|
||||||
|
<string name="screen_invites_decline_direct_chat_title">"Recusar chat"</string>
|
||||||
|
<string name="screen_invites_empty_list">"Sem convites"</string>
|
||||||
|
<string name="screen_invites_invited_you">"%1$s(%2$s) convidou você"</string>
|
||||||
|
<string name="screen_migration_message">"Este é um processo único, obrigado por esperar."</string>
|
||||||
|
<string name="screen_migration_title">"Configurando sua conta."</string>
|
||||||
|
<string name="screen_roomlist_a11y_create_message">"Criar uma nova conversa ou sala"</string>
|
||||||
|
<string name="screen_roomlist_empty_message">"Comece enviando uma mensagem para alguém."</string>
|
||||||
|
<string name="screen_roomlist_empty_title">"Ainda não há conversas."</string>
|
||||||
|
<string name="screen_roomlist_filter_people">"Pessoas"</string>
|
||||||
|
<string name="screen_roomlist_main_space_title">"Conversas"</string>
|
||||||
|
<string name="session_verification_banner_message">"Parece que você está usando um novo dispositivo. Verifique com outro dispositivo para acessar suas mensagens criptografadas."</string>
|
||||||
|
<string name="session_verification_banner_title">"Verifique se é você"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -441,7 +441,10 @@ class RoomListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
||||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
val room = FakeMatrixRoom()
|
val setIsFavoriteResult = lambdaRecorder { _: Boolean -> Result.success(Unit) }
|
||||||
|
val room = FakeMatrixRoom(
|
||||||
|
setIsFavoriteResult = setIsFavoriteResult
|
||||||
|
)
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val client = FakeMatrixClient().apply {
|
val client = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
|
|
@ -452,9 +455,13 @@ class RoomListPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, true))
|
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, true))
|
||||||
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true))
|
setIsFavoriteResult.assertions().isCalledOnce().with(value(true))
|
||||||
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, false))
|
initialState.eventSink(RoomListEvents.SetRoomIsFavorite(A_ROOM_ID, false))
|
||||||
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false))
|
setIsFavoriteResult.assertions().isCalledExactly(2)
|
||||||
|
.withSequence(
|
||||||
|
listOf(value(true)),
|
||||||
|
listOf(value(false)),
|
||||||
|
)
|
||||||
assertThat(analyticsService.capturedEvents).containsExactly(
|
assertThat(analyticsService.capturedEvents).containsExactly(
|
||||||
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle),
|
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle),
|
||||||
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle)
|
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuFavouriteToggle)
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,14 @@
|
||||||
<string name="screen_recovery_key_change_success">"აღდგენის გასაღები შეიცვალა"</string>
|
<string name="screen_recovery_key_change_success">"აღდგენის გასაღები შეიცვალა"</string>
|
||||||
<string name="screen_recovery_key_change_title">"გსურთ აღდგენის გასაღების შეცვლა?"</string>
|
<string name="screen_recovery_key_change_title">"გსურთ აღდგენის გასაღების შეცვლა?"</string>
|
||||||
<string name="screen_recovery_key_confirm_description">"დარწმუნდით, რომ ვერავინ ხედავს ამ ეკრანს!"</string>
|
<string name="screen_recovery_key_confirm_description">"დარწმუნდით, რომ ვერავინ ხედავს ამ ეკრანს!"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_error_content">"გთხოვთ, სცადოთ ხელახლა, რათა თქვენი ჩეთის სარეზერვო ასლაზე წვდომა დაადასტუროთ"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_error_title">"აღდგენის არასწორი გასაღები"</string>
|
||||||
<string name="screen_recovery_key_confirm_key_description">"თუ თქვენ გაქვთ უსაფრთხოების გასაღები ან უსაფრთხოების ფრაზა, ეს ასევე იმუშავებს."</string>
|
<string name="screen_recovery_key_confirm_key_description">"თუ თქვენ გაქვთ უსაფრთხოების გასაღები ან უსაფრთხოების ფრაზა, ეს ასევე იმუშავებს."</string>
|
||||||
<string name="screen_recovery_key_confirm_key_placeholder">"შეყვანა"</string>
|
<string name="screen_recovery_key_confirm_key_placeholder">"შეყვანა"</string>
|
||||||
<string name="screen_recovery_key_confirm_success">"აღდგენის გასაღები დადასტურებულია"</string>
|
<string name="screen_recovery_key_confirm_success">"აღდგენის გასაღები დადასტურებულია"</string>
|
||||||
<string name="screen_recovery_key_confirm_title">"შეიყვანეთ თქვენი აღდგენის გასაღები"</string>
|
<string name="screen_recovery_key_confirm_title">"შეიყვანეთ თქვენი აღდგენის გასაღები"</string>
|
||||||
|
<string name="screen_recovery_key_copied_to_clipboard">"დაკოპირებულია აღდგენის გასაღები"</string>
|
||||||
|
<string name="screen_recovery_key_generating_key">"გენერირება…"</string>
|
||||||
<string name="screen_recovery_key_save_action">"აღდგენის გასაღების შენახვა"</string>
|
<string name="screen_recovery_key_save_action">"აღდგენის გასაღების შენახვა"</string>
|
||||||
<string name="screen_recovery_key_save_description">"ჩაწერეთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას ან შეინახეთ პაროლის მენეჯერში."</string>
|
<string name="screen_recovery_key_save_description">"ჩაწერეთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას ან შეინახეთ პაროლის მენეჯერში."</string>
|
||||||
<string name="screen_recovery_key_save_key_description">"აღდგენის გასაღების დასაკოპირებლად, დააწკაპუნეთ"</string>
|
<string name="screen_recovery_key_save_key_description">"აღდგენის გასაღების დასაკოპირებლად, დააწკაპუნეთ"</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_chat_backup_key_backup_action_disable">"Wyłącz backup"</string>
|
||||||
|
<string name="screen_chat_backup_key_backup_action_enable">"Włącz backup"</string>
|
||||||
|
<string name="screen_chat_backup_key_backup_description">"Backup zapewnia, że nie stracisz swojej historii wiadomości. %1$s"</string>
|
||||||
|
<string name="screen_chat_backup_key_backup_title">"Backup"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_change">"Zmień klucz przywracania"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_confirm">"Wprowadź klucz przywracania"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_confirm_description">"Backup czatu nie jest zsynchronizowany."</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_setup">"Skonfiguruj przywracanie"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_setup_description">"Uzyskaj dostęp do swoich wiadomości szyfrowanych, jeśli utracisz wszystkie swoje urządzenia lub zostaniesz wylogowany z %1$s."</string>
|
||||||
|
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Wyłącz"</string>
|
||||||
|
<string name="screen_key_backup_disable_confirmation_description">"Utracisz dostęp do wiadomości szyfrowanych, jeśli zostaniesz wylogowany ze wszystkich urządzeń."</string>
|
||||||
|
<string name="screen_key_backup_disable_confirmation_title">"Czy na pewno chcesz wyłączyć backup?"</string>
|
||||||
|
<string name="screen_key_backup_disable_description">"Wyłączenie backupu spowoduje usunięcie kopii klucza szyfrowania i wyłączenie innych funkcji bezpieczeństwa. W takim przypadku będziesz:"</string>
|
||||||
|
<string name="screen_key_backup_disable_description_point_1">"Posiadał historii wiadomości szyfrowanych na nowych urządzeniach"</string>
|
||||||
|
<string name="screen_key_backup_disable_description_point_2">"Utracisz dostęp do wiadomości szyfrowanych, jeśli zostaniesz wszędzie wylogowany z %1$s"</string>
|
||||||
|
<string name="screen_key_backup_disable_title">"Czy na pewno chcesz wyłączyć backup?"</string>
|
||||||
|
<string name="screen_recovery_key_change_description">"Uzyskaj nowy klucz przywracania, jeśli straciłeś dostęp do obecnego. Po zmianie klucza przywracania stary nie będzie już działał."</string>
|
||||||
|
<string name="screen_recovery_key_change_generate_key">"Generuj nowy klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_change_generate_key_description">"Upewnij się, że klucz przywracania będzie trzymany w bezpiecznym miejscu"</string>
|
||||||
|
<string name="screen_recovery_key_change_success">"Zmieniono klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_change_title">"Zmienić klucz przywracania?"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_description">"Upewnij się, że nikt nie widzi tego ekranu!"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_error_content">"Spróbuj ponownie, aby potwierdzić dostęp do backupu czatu."</string>
|
||||||
|
<string name="screen_recovery_key_confirm_error_title">"Nieprawidłowy klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_key_description">"To też zadziała, jeśli posiadasz klucz lub frazę bezpieczeństwa."</string>
|
||||||
|
<string name="screen_recovery_key_confirm_key_placeholder">"Wprowadź…"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_success">"Potwierdzono klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_title">"Wprowadź klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_copied_to_clipboard">"Skopiowano klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_generating_key">"Generuję…"</string>
|
||||||
|
<string name="screen_recovery_key_save_action">"Zapisz klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_save_description">"Zapisz klucz przywracania w bezpiecznym miejscu lub zapisz go w menedżerze haseł."</string>
|
||||||
|
<string name="screen_recovery_key_save_key_description">"Stuknij, by skopiować klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_save_title">"Zapisz klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_setup_confirmation_description">"Po tym kroku nie będziesz mieć dostępu do nowego klucza przywracania."</string>
|
||||||
|
<string name="screen_recovery_key_setup_confirmation_title">"Czy zapisałeś swój klucz przywracania?"</string>
|
||||||
|
<string name="screen_recovery_key_setup_description">"Backup czatu jest chroniony przez klucz przywracania. Jeśli potrzebujesz utworzyć nowy klucz, możesz to zrobić wybierając `Zmień klucz przywracania`."</string>
|
||||||
|
<string name="screen_recovery_key_setup_generate_key">"Wygeneruj klucz przywracania"</string>
|
||||||
|
<string name="screen_recovery_key_setup_generate_key_description">"Upewnij się, że klucz przywracania możesz przechowywać w bezpiecznym miejscu"</string>
|
||||||
|
<string name="screen_recovery_key_setup_success">"Skonfigurowano przywracanie pomyślnie"</string>
|
||||||
|
<string name="screen_recovery_key_setup_title">"Skonfiguruj przywracanie"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="screen_chat_backup_key_backup_action_disable">"Desativar o backup"</string>
|
||||||
|
<string name="screen_chat_backup_key_backup_action_enable">"Ativar o backup"</string>
|
||||||
|
<string name="screen_chat_backup_key_backup_description">"O backup garante que você não perca seu histórico de mensagens. %1$s."</string>
|
||||||
|
<string name="screen_chat_backup_key_backup_title">"Backup"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_change">"Mudar chave de recuperação"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_confirm">"Insira a chave de recuperação"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_confirm_description">"Seu backup das conversas está atualmente fora de sincronia."</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_setup">"Configurar a recuperação"</string>
|
||||||
|
<string name="screen_chat_backup_recovery_action_setup_description">"Tenha acesso às suas mensagens criptografadas se você perder todos os seus dispositivos ou for desconectado do %1$s em qualquer lugar."</string>
|
||||||
|
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Desligar"</string>
|
||||||
|
<string name="screen_key_backup_disable_confirmation_description">"Você perderá suas mensagens criptografadas se estiver desconectado de todos os dispositivos."</string>
|
||||||
|
<string name="screen_key_backup_disable_confirmation_title">"Tem certeza de que deseja desativar o backup?"</string>
|
||||||
|
<string name="screen_key_backup_disable_description">"Desativar o backup removerá o backup da chave de criptografia atual e desativará outros recursos de segurança. Neste caso, você irá:"</string>
|
||||||
|
<string name="screen_key_backup_disable_description_point_1">"Não ter histórico de mensagens criptografadas em novos dispositivos"</string>
|
||||||
|
<string name="screen_key_backup_disable_description_point_2">"Perder o acesso às suas mensagens criptografadas se você estiver desconectado %1$s em todos os lugares"</string>
|
||||||
|
<string name="screen_key_backup_disable_title">"Tem certeza de que deseja desativar o backup?"</string>
|
||||||
|
<string name="screen_recovery_key_change_description">"Obtenha uma nova chave de recuperação caso tenha perdido a existente. Depois de alterar sua chave de recuperação, a antiga não funcionará mais."</string>
|
||||||
|
<string name="screen_recovery_key_change_generate_key">"Gere uma nova chave de recuperação"</string>
|
||||||
|
<string name="screen_recovery_key_change_generate_key_description">"Certifique-se de que você pode armazenar sua chave de recuperação em algum lugar seguro"</string>
|
||||||
|
<string name="screen_recovery_key_change_success">"Chave de recuperação alterada"</string>
|
||||||
|
<string name="screen_recovery_key_change_title">"Alterar chave de recuperação?"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_description">"Certifique-se de que ninguém possa ver essa tela!"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_key_description">"Se você tiver uma chave de segurança ou frase de segurança, isso também funcionará."</string>
|
||||||
|
<string name="screen_recovery_key_confirm_key_placeholder">"Inserir…"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_success">"Chave de recuperação confirmada"</string>
|
||||||
|
<string name="screen_recovery_key_confirm_title">"Insira sua chave de recuperação"</string>
|
||||||
|
<string name="screen_recovery_key_save_action">"Salvar chave de recuperação"</string>
|
||||||
|
<string name="screen_recovery_key_save_description">"Anote sua chave de recuperação em algum lugar seguro ou salve-a em um gerenciador de senhas."</string>
|
||||||
|
<string name="screen_recovery_key_save_key_description">"Toque para copiar a chave de recuperação"</string>
|
||||||
|
<string name="screen_recovery_key_save_title">"Salve sua chave de recuperação"</string>
|
||||||
|
<string name="screen_recovery_key_setup_confirmation_description">"Você não poderá acessar sua nova chave de recuperação após essa etapa."</string>
|
||||||
|
<string name="screen_recovery_key_setup_confirmation_title">"Você salvou sua chave de recuperação?"</string>
|
||||||
|
<string name="screen_recovery_key_setup_description">"Seu backup das conversas é protegido por uma chave de recuperação. Se precisar de uma nova chave de recuperação após a configuração, você pode recriá-la selecionando “Alterar chave de recuperação”."</string>
|
||||||
|
<string name="screen_recovery_key_setup_generate_key">"Gere sua chave de recuperação"</string>
|
||||||
|
<string name="screen_recovery_key_setup_generate_key_description">"Certifique-se de que você pode armazenar sua chave de recuperação em algum lugar seguro"</string>
|
||||||
|
<string name="screen_recovery_key_setup_success">"Configuração de recuperação bem-sucedida"</string>
|
||||||
|
<string name="screen_recovery_key_setup_title">"Configurar a recuperação"</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||||
|
|
@ -92,7 +93,9 @@ class SharePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send text ok`() = runTest {
|
fun `present - send text ok`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom()
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
sendMessageResult = { _, _, _ -> Result.success(Unit) },
|
||||||
|
)
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, matrixRoom)
|
givenGetRoomResult(A_ROOM_ID, matrixRoom)
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +120,9 @@ class SharePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send media ok`() = runTest {
|
fun `present - send media ok`() = runTest {
|
||||||
val matrixRoom = FakeMatrixRoom()
|
val matrixRoom = FakeMatrixRoom(
|
||||||
|
sendMediaResult = { Result.success(FakeMediaUploadHandler()) },
|
||||||
|
)
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, matrixRoom)
|
givenGetRoomResult(A_ROOM_ID, matrixRoom)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue