Merge branch 'develop' into feature/jme/add-simplified-sliding-sync-toggle
This commit is contained in:
2
.github/workflows/build_enterprise.yml
vendored
2
.github/workflows/build_enterprise.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
name: Build Enterprise APKs
|
||||
runs-on: ubuntu-latest
|
||||
# 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:
|
||||
matrix:
|
||||
variant: [debug, release, nightly]
|
||||
|
||||
4
.github/workflows/danger.yml
vendored
4
.github/workflows/danger.yml
vendored
@@ -6,6 +6,8 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
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:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
@@ -13,7 +15,7 @@ jobs:
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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: |
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
# 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:
|
||||
- name: ⏬ Checkout with LFS
|
||||
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
|
||||
uses: gradle-update/update-gradle-wrapper-action@v1
|
||||
# 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:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
target-branch: develop
|
||||
|
||||
19
.github/workflows/quality.yml
vendored
19
.github/workflows/quality.yml
vendored
@@ -20,10 +20,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: Run code quality check suite
|
||||
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 }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: Use JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
@@ -116,10 +118,11 @@ jobs:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: Use JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
@@ -159,10 +162,11 @@ jobs:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: Use JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
@@ -198,10 +202,11 @@ jobs:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: Use JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
@@ -237,10 +242,11 @@ jobs:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: Use JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
@@ -271,6 +277,7 @@ jobs:
|
||||
name: Project Check Suite
|
||||
runs-on: ubuntu-latest
|
||||
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:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -42,6 +42,7 @@ jobs:
|
||||
enterprise:
|
||||
name: Create App Bundle Enterprise
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
concurrency:
|
||||
group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
|
||||
cancel-in-progress: true
|
||||
@@ -49,6 +50,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- name: Clone submodules
|
||||
|
||||
2
.github/workflows/sync-localazy.yml
vendored
2
.github/workflows/sync-localazy.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
sync-localazy:
|
||||
runs-on: ubuntu-latest
|
||||
# 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:
|
||||
- uses: actions/checkout@v4
|
||||
- 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:
|
||||
runs-on: ubuntu-latest
|
||||
# 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.
|
||||
steps:
|
||||
- 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 }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
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:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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
|
||||
- name: ☕️ Use JDK 17
|
||||
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)
|
||||
=========================================
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
<locale android:name="in"/>
|
||||
<locale android:name="it"/>
|
||||
<locale android:name="ka"/>
|
||||
<locale android:name="pl"/>
|
||||
<locale android:name="pt"/>
|
||||
<locale android:name="pt_BR"/>
|
||||
<locale android:name="ro"/>
|
||||
<locale android:name="ru"/>
|
||||
<locale android:name="sk"/>
|
||||
|
||||
@@ -123,7 +123,9 @@ class JoinRoomLoadedFlowNodeTest {
|
||||
@Test
|
||||
fun `given a room flow node when initialized then it loads messages entry point`() = runTest {
|
||||
// GIVEN
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
updateMembersResult = { }
|
||||
)
|
||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
|
||||
val roomFlowNode = createJoinedRoomLoadedFlowNode(
|
||||
@@ -144,7 +146,9 @@ class JoinRoomLoadedFlowNodeTest {
|
||||
@Test
|
||||
fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest {
|
||||
// GIVEN
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
updateMembersResult = { }
|
||||
)
|
||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
||||
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class) class SendQueuesTest {
|
||||
private val matrixClient = FakeMatrixClient()
|
||||
private val room = FakeMatrixRoom()
|
||||
private val networkMonitor = FakeNetworkMonitor()
|
||||
private val sut = SendQueues(matrixClient, networkMonitor)
|
||||
|
||||
@@ -43,11 +42,11 @@ import org.junit.Test
|
||||
val setAllSendQueuesEnabledLambda = lambdaRecorder { _: Boolean -> }
|
||||
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
|
||||
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
|
||||
matrixClient.givenGetRoomResult(room.roomId, room)
|
||||
|
||||
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
|
||||
room.setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
||||
|
||||
val room = FakeMatrixRoom(
|
||||
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
||||
)
|
||||
matrixClient.givenGetRoomResult(room.roomId, room)
|
||||
sut.launchIn(backgroundScope)
|
||||
|
||||
sendQueueDisabledFlow.emit(room.roomId)
|
||||
@@ -72,10 +71,11 @@ import org.junit.Test
|
||||
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
|
||||
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
|
||||
networkMonitor.connectivity.value = NetworkStatus.Offline
|
||||
matrixClient.givenGetRoomResult(room.roomId, room)
|
||||
|
||||
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
|
||||
room.setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
||||
val room = FakeMatrixRoom(
|
||||
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
|
||||
)
|
||||
matrixClient.givenGetRoomResult(room.roomId, room)
|
||||
|
||||
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.libraries.di.AppScope
|
||||
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.NotificationIdProvider
|
||||
import io.element.android.libraries.push.api.notifications.OnMissedCallNotificationHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
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 timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
@@ -79,11 +90,16 @@ class DefaultActiveCallManager @Inject constructor(
|
||||
private val onMissedCallNotificationHandler: OnMissedCallNotificationHandler,
|
||||
private val ringingCallNotificationCreator: RingingCallNotificationCreator,
|
||||
private val notificationManagerCompat: NotificationManagerCompat,
|
||||
private val matrixClientProvider: MatrixClientProvider,
|
||||
) : ActiveCallManager {
|
||||
private var timedOutCallJob: Job? = null
|
||||
|
||||
override val activeCall = MutableStateFlow<ActiveCall?>(null)
|
||||
|
||||
init {
|
||||
observeRingingCall()
|
||||
}
|
||||
|
||||
override fun registerIncomingCall(notificationData: CallNotificationData) {
|
||||
if (activeCall.value != null) {
|
||||
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_message_android">"Tippen, um zum Anruf zurückzukehren"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Anruf läuft"</string>
|
||||
<string name="screen_incoming_call_subtitle_android">"Eingehender Element Call"</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">"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_2
|
||||
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.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.NotificationIdProvider
|
||||
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.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
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.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
@@ -59,26 +66,28 @@ class DefaultActiveCallManagerTest {
|
||||
@Test
|
||||
fun `registerIncomingCall - sets the incoming call as active`() = runTest {
|
||||
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()
|
||||
manager.registerIncomingCall(callNotificationData)
|
||||
val callNotificationData = aCallNotificationData()
|
||||
manager.registerIncomingCall(callNotificationData)
|
||||
|
||||
assertThat(manager.activeCall.value).isEqualTo(
|
||||
ActiveCall(
|
||||
callType = CallType.RoomCall(
|
||||
sessionId = callNotificationData.sessionId,
|
||||
roomId = callNotificationData.roomId,
|
||||
),
|
||||
callState = CallState.Ringing(callNotificationData)
|
||||
assertThat(manager.activeCall.value).isEqualTo(
|
||||
ActiveCall(
|
||||
callType = CallType.RoomCall(
|
||||
sessionId = callNotificationData.sessionId,
|
||||
roomId = callNotificationData.roomId,
|
||||
),
|
||||
callState = CallState.Ringing(callNotificationData)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
runCurrent()
|
||||
|
||||
verify { notificationManagerCompat.notify(notificationId, any()) }
|
||||
verify { notificationManagerCompat.notify(notificationId, any()) }
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@@ -86,38 +95,42 @@ class DefaultActiveCallManagerTest {
|
||||
fun `registerIncomingCall - when there is an already active call adds missed call notification`() = runTest {
|
||||
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
||||
val onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
||||
val manager = createActiveCallManager(
|
||||
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
||||
)
|
||||
inCancellableScope {
|
||||
val manager = createActiveCallManager(
|
||||
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
||||
)
|
||||
|
||||
// Register existing call
|
||||
val callNotificationData = aCallNotificationData()
|
||||
manager.registerIncomingCall(callNotificationData)
|
||||
val activeCall = manager.activeCall.value
|
||||
// Register existing call
|
||||
val callNotificationData = aCallNotificationData()
|
||||
manager.registerIncomingCall(callNotificationData)
|
||||
val activeCall = manager.activeCall.value
|
||||
|
||||
// Now add a new call
|
||||
manager.registerIncomingCall(aCallNotificationData(roomId = A_ROOM_ID_2))
|
||||
// Now add a new call
|
||||
manager.registerIncomingCall(aCallNotificationData(roomId = A_ROOM_ID_2))
|
||||
|
||||
assertThat(manager.activeCall.value).isEqualTo(activeCall)
|
||||
assertThat((manager.activeCall.value?.callType as? CallType.RoomCall)?.roomId).isNotEqualTo(A_ROOM_ID_2)
|
||||
assertThat(manager.activeCall.value).isEqualTo(activeCall)
|
||||
assertThat((manager.activeCall.value?.callType as? CallType.RoomCall)?.roomId).isNotEqualTo(A_ROOM_ID_2)
|
||||
|
||||
advanceTimeBy(1)
|
||||
advanceTimeBy(1)
|
||||
|
||||
addMissedCallNotificationLambda.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_SESSION_ID), value(A_ROOM_ID_2), value(AN_EVENT_ID))
|
||||
addMissedCallNotificationLambda.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_SESSION_ID), value(A_ROOM_ID_2), value(AN_EVENT_ID))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `incomingCallTimedOut - when there isn't an active call does nothing`() = runTest {
|
||||
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
||||
val manager = createActiveCallManager(
|
||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
||||
)
|
||||
inCancellableScope {
|
||||
val manager = createActiveCallManager(
|
||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda)
|
||||
)
|
||||
|
||||
manager.incomingCallTimedOut()
|
||||
manager.incomingCallTimedOut()
|
||||
|
||||
addMissedCallNotificationLambda.assertions().isNeverCalled()
|
||||
addMissedCallNotificationLambda.assertions().isNeverCalled()
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||
val addMissedCallNotificationLambda = lambdaRecorder<SessionId, RoomId, EventId, Unit> { _, _, _ -> }
|
||||
val manager = createActiveCallManager(
|
||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda),
|
||||
notificationManagerCompat = notificationManagerCompat,
|
||||
)
|
||||
inCancellableScope {
|
||||
val manager = createActiveCallManager(
|
||||
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(addMissedCallNotificationLambda = addMissedCallNotificationLambda),
|
||||
notificationManagerCompat = notificationManagerCompat,
|
||||
)
|
||||
|
||||
manager.registerIncomingCall(aCallNotificationData())
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
manager.registerIncomingCall(aCallNotificationData())
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
|
||||
manager.incomingCallTimedOut()
|
||||
advanceTimeBy(1)
|
||||
manager.incomingCallTimedOut()
|
||||
advanceTimeBy(1)
|
||||
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
addMissedCallNotificationLambda.assertions().isCalledOnce()
|
||||
verify { notificationManagerCompat.cancel(notificationId) }
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
addMissedCallNotificationLambda.assertions().isCalledOnce()
|
||||
verify { notificationManagerCompat.cancel(notificationId) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hungUpCall - removes existing call if the CallType matches`() = runTest {
|
||||
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()
|
||||
manager.registerIncomingCall(notificationData)
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
val notificationData = aCallNotificationData()
|
||||
manager.registerIncomingCall(notificationData)
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
|
||||
manager.hungUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
manager.hungUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
|
||||
verify { notificationManagerCompat.cancel(notificationId) }
|
||||
verify { notificationManagerCompat.cancel(notificationId) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hungUpCall - does nothing if the CallType doesn't match`() = runTest {
|
||||
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())
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
manager.registerIncomingCall(aCallNotificationData())
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
|
||||
manager.hungUpCall(CallType.ExternalUrl("https://example.com"))
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
manager.hungUpCall(CallType.ExternalUrl("https://example.com"))
|
||||
assertThat(manager.activeCall.value).isNotNull()
|
||||
|
||||
verify(exactly = 0) { notificationManagerCompat.cancel(notificationId) }
|
||||
verify(exactly = 0) { notificationManagerCompat.cancel(notificationId) }
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `joinedCall - register an ongoing call and tries sending the call notify event`() = runTest {
|
||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||
val manager = createActiveCallManager(
|
||||
notificationManagerCompat = notificationManagerCompat,
|
||||
)
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
inCancellableScope {
|
||||
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
|
||||
manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID))
|
||||
assertThat(manager.activeCall.value).isEqualTo(
|
||||
ActiveCall(
|
||||
callType = CallType.RoomCall(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
),
|
||||
callState = CallState.InCall,
|
||||
manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID))
|
||||
assertThat(manager.activeCall.value).isEqualTo(
|
||||
ActiveCall(
|
||||
callType = CallType.RoomCall(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
),
|
||||
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(),
|
||||
onMissedCallNotificationHandler: FakeOnMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler(),
|
||||
notificationManagerCompat: NotificationManagerCompat = mockk(relaxed = true),
|
||||
coroutineScope: CoroutineScope = this,
|
||||
) = DefaultActiveCallManager(
|
||||
coroutineScope = this,
|
||||
coroutineScope = coroutineScope,
|
||||
onMissedCallNotificationHandler = onMissedCallNotificationHandler,
|
||||
ringingCallNotificationCreator = RingingCallNotificationCreator(
|
||||
context = InstrumentationRegistry.getInstrumentation().targetContext,
|
||||
@@ -209,5 +307,6 @@ class DefaultActiveCallManagerTest {
|
||||
notificationBitmapLoader = FakeNotificationBitmapLoader(),
|
||||
),
|
||||
notificationManagerCompat = notificationManagerCompat,
|
||||
matrixClientProvider = matrixClientProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ class DefaultCallWidgetProviderTest {
|
||||
|
||||
@Test
|
||||
fun `getWidget - fails if it can't generate the URL for the widget`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenGenerateWidgetWebViewUrlResult(Result.failure(Exception("Can't generate URL for widget")))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.failure(Exception("Can't generate URL for widget")) }
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
@@ -66,10 +66,10 @@ class DefaultCallWidgetProviderTest {
|
||||
|
||||
@Test
|
||||
fun `getWidget - fails if it can't get the widget driver`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
||||
givenGetWidgetDriverResult(Result.failure(Exception("Can't get a widget driver")))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||
getWidgetDriverResult = { Result.failure(Exception("Can't get a widget driver")) }
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
@@ -79,10 +79,10 @@ class DefaultCallWidgetProviderTest {
|
||||
|
||||
@Test
|
||||
fun `getWidget - returns a widget driver when all steps are successful`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
||||
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||
getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
@@ -92,10 +92,10 @@ class DefaultCallWidgetProviderTest {
|
||||
|
||||
@Test
|
||||
fun `getWidget - will use a custom base url if it exists`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
||||
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||
getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
@@ -120,10 +120,10 @@ class DefaultCallWidgetProviderTest {
|
||||
val elementCallBaseUrlProvider = FakeElementCallBaseUrlProvider { matrixClient ->
|
||||
providesLambda(matrixClient)
|
||||
}
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenGenerateWidgetWebViewUrlResult(Result.success("url"))
|
||||
givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
generateWidgetWebViewUrlResult = { _, _, _, _ -> Result.success("url") },
|
||||
getWidgetDriverResult = { Result.success(FakeMatrixWidgetDriver()) },
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
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 {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
result = FakeMatrixRoom(
|
||||
leaveRoomLambda = declineInviteFailure
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
@@ -142,9 +142,9 @@ class AcceptDeclineInvitePresenterTest {
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
result = FakeMatrixRoom(
|
||||
leaveRoomLambda = declineInviteSuccess
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
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 {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom(),
|
||||
result = FakeMatrixRoom(
|
||||
leaveRoomLambda = { Result.success(Unit) }
|
||||
),
|
||||
)
|
||||
},
|
||||
roomMembershipObserver = roomMembershipObserver
|
||||
@@ -162,9 +164,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||
client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
||||
},
|
||||
result = FakeMatrixRoom(
|
||||
leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
||||
),
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -186,7 +188,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||
client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom(),
|
||||
result = FakeMatrixRoom(
|
||||
leaveRoomLambda = { Result.success(Unit) }
|
||||
),
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -208,9 +212,9 @@ class DefaultLeaveRoomPresenterTest {
|
||||
client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom().apply {
|
||||
this.leaveRoomLambda = { Result.failure(RuntimeException("Blimey!")) }
|
||||
},
|
||||
result = FakeMatrixRoom(
|
||||
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.PermissionsState
|
||||
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.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.SendLocationInvocation
|
||||
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
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.test.runTest
|
||||
import org.junit.Rule
|
||||
@@ -46,16 +48,18 @@ class SendLocationPresenterTest {
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
private val fakePermissionsPresenter = FakePermissionsPresenter()
|
||||
private val fakeMatrixRoom = FakeMatrixRoom()
|
||||
private val fakeAnalyticsService = FakeAnalyticsService()
|
||||
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
||||
private val fakeLocationActions = FakeLocationActions()
|
||||
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
||||
private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter(
|
||||
|
||||
private fun createSendLocationPresenter(
|
||||
matrixRoom: MatrixRoom = FakeMatrixRoom(),
|
||||
): SendLocationPresenter = SendLocationPresenter(
|
||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
||||
},
|
||||
room = fakeMatrixRoom,
|
||||
room = matrixRoom,
|
||||
analyticsService = fakeAnalyticsService,
|
||||
messageComposerContext = fakeMessageComposerContext,
|
||||
locationActions = fakeLocationActions,
|
||||
@@ -64,6 +68,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `initial state with permissions granted`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.AllGranted,
|
||||
@@ -90,6 +95,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `initial state with permissions partially granted`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.SomeGranted,
|
||||
@@ -116,6 +122,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `initial state with permissions denied`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -142,6 +149,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `initial state with permissions denied once`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -168,6 +176,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `rationale dialog dismiss`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -199,6 +208,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `rationale dialog continue`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -227,6 +237,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `permission denied dialog dismiss`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -258,6 +269,13 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.AllGranted,
|
||||
@@ -289,16 +307,14 @@ class SendLocationPresenterTest {
|
||||
|
||||
delay(1) // Wait for the coroutine to finish
|
||||
|
||||
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
|
||||
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
|
||||
SendLocationInvocation(
|
||||
body = "Location was shared at geo:3.0,4.0;u=5.0",
|
||||
geoUri = "geo:3.0,4.0;u=5.0",
|
||||
description = null,
|
||||
zoomLevel = 15,
|
||||
assetType = AssetType.SENDER
|
||||
sendLocationResult.assertions().isCalledOnce()
|
||||
.with(
|
||||
value("Location was shared at geo:3.0,4.0;u=5.0"),
|
||||
value("geo:3.0,4.0;u=5.0"),
|
||||
value(null),
|
||||
value(15),
|
||||
value(AssetType.SENDER),
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
||||
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
||||
@@ -314,6 +330,13 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -345,16 +368,14 @@ class SendLocationPresenterTest {
|
||||
|
||||
delay(1) // Wait for the coroutine to finish
|
||||
|
||||
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
|
||||
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
|
||||
SendLocationInvocation(
|
||||
body = "Location was shared at geo:0.0,1.0",
|
||||
geoUri = "geo:0.0,1.0",
|
||||
description = null,
|
||||
zoomLevel = 15,
|
||||
assetType = AssetType.PIN
|
||||
sendLocationResult.assertions().isCalledOnce()
|
||||
.with(
|
||||
value("Location was shared at geo:0.0,1.0"),
|
||||
value("geo:0.0,1.0"),
|
||||
value(null),
|
||||
value(15),
|
||||
value(AssetType.PIN),
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
||||
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
||||
@@ -370,6 +391,13 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -418,6 +446,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `open settings activity`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
fakePermissionsPresenter.givenState(
|
||||
aPermissionsState(
|
||||
permissions = PermissionsState.Permissions.NoneGranted,
|
||||
@@ -452,6 +481,7 @@ class SendLocationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `application name is in state`() = runTest {
|
||||
val sendLocationPresenter = createSendLocationPresenter()
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
sendLocationPresenter.present()
|
||||
}.test {
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
<?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">"ბიომეტრიული ავტორიზაცია"</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_settings_change_pin">"PIN კოდის შეცვლა"</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_alert_message">"დარწმუნებული ხართ, რომ გსურთ 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_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_title">"თქვენ ახლა გადიხართ…"</string>
|
||||
<plurals name="screen_app_lock_subtitle">
|
||||
@@ -17,5 +31,7 @@
|
||||
<item quantity="one">"არასწორი PIN. თქვენ %1$d შანსი დაგრჩათ"</item>
|
||||
<item quantity="other">"არასწორი PIN. თქვენ %1$d შანსი დაგრჩათ"</item>
|
||||
</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>
|
||||
</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_title">"კეთილი იყოს თქვენი მობრძანება!"</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_message_login_element_dot_io">"კერძო სერვერი Element-ის თანამშრომლებისთვის."</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_title">"გამოსვლა"</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_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>
|
||||
|
||||
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.ComposerDraftType
|
||||
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.reply.InReplyToDetails
|
||||
import io.element.android.libraries.matrix.ui.messages.reply.map
|
||||
@@ -436,7 +437,14 @@ class MessageComposerPresenter @Inject constructor(
|
||||
val eventId = capturedMode.eventId
|
||||
val transactionId = capturedMode.transactionId
|
||||
timelineController.invokeOnCurrentTimeline {
|
||||
// First try to edit the message in the current timeline
|
||||
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) {
|
||||
when (event) {
|
||||
is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary(
|
||||
reactions = event.reactions,
|
||||
reactions = event.reactions.toImmutableList(),
|
||||
selectedKey = event.selectedKey,
|
||||
selectedEventId = event.eventId
|
||||
)
|
||||
@@ -73,8 +73,8 @@ class ReactionSummaryPresenter @Inject constructor(
|
||||
avatarUrl = member?.avatarUrl
|
||||
)
|
||||
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.libraries.matrix.api.core.EventId
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class ReactionSummaryState(
|
||||
val target: Summary?,
|
||||
val eventSink: (ReactionSummaryEvents) -> Unit
|
||||
) {
|
||||
data class Summary(
|
||||
val reactions: List<AggregatedReaction>,
|
||||
val reactions: ImmutableList<AggregatedReaction>,
|
||||
val selectedKey: String,
|
||||
val selectedEventId: EventId
|
||||
)
|
||||
|
||||
@@ -264,27 +264,27 @@ class TimelineItemContentMessageFactory @Inject constructor(
|
||||
}
|
||||
|
||||
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
|
||||
val oldURLSpans = getSpans<URLSpan>(0, length).associateWith {
|
||||
val start = getSpanStart(it)
|
||||
val end = getSpanEnd(it)
|
||||
val oldURLSpans = spannable.getSpans<URLSpan>(0, length).associateWith {
|
||||
val start = spannable.getSpanStart(it)
|
||||
val end = spannable.getSpanEnd(it)
|
||||
Pair(start, end)
|
||||
}
|
||||
// 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
|
||||
for ((urlSpan, location) in oldURLSpans) {
|
||||
val (start, end) = location
|
||||
val addedSpans = getSpans<URLSpan>(start, end).orEmpty()
|
||||
val addedSpans = spannable.getSpans<URLSpan>(start, end).orEmpty()
|
||||
if (addedSpans.isNotEmpty()) {
|
||||
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),
|
||||
)
|
||||
}
|
||||
.toImmutableList()
|
||||
)
|
||||
}
|
||||
// Sort aggregated reactions by count and then timestamp ascending, using
|
||||
@@ -127,7 +128,9 @@ class TimelineItemEventFactory @Inject constructor(
|
||||
compareByDescending<AggregatedReaction> { it.count }
|
||||
.thenBy { it.senders[0].timestamp }
|
||||
)
|
||||
return TimelineItemReactions(aggregatedReactions.toImmutableList())
|
||||
return TimelineItemReactions(
|
||||
reactions = aggregatedReactions.toImmutableList()
|
||||
)
|
||||
}
|
||||
|
||||
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.matrix.api.core.UserId
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
* Length at which we ellipsize a reaction key for display
|
||||
@@ -35,28 +36,22 @@ private const val MAX_DISPLAY_CHARS = 16
|
||||
data class AggregatedReaction(
|
||||
val currentUserId: UserId,
|
||||
val key: String,
|
||||
val senders: List<AggregatedReactionSender>
|
||||
val senders: ImmutableList<AggregatedReactionSender>
|
||||
) {
|
||||
/**
|
||||
* The key to be displayed on screen.
|
||||
*
|
||||
* See [MAX_DISPLAY_CHARS].
|
||||
*/
|
||||
val displayKey: String by lazy {
|
||||
key.ellipsize(MAX_DISPLAY_CHARS)
|
||||
}
|
||||
val displayKey: String = key.ellipsize(MAX_DISPLAY_CHARS)
|
||||
|
||||
/**
|
||||
* The number of users who reacted with this key.
|
||||
*/
|
||||
val count: Int by lazy {
|
||||
senders.count()
|
||||
}
|
||||
val count: Int = senders.count()
|
||||
|
||||
/**
|
||||
* True if the reaction has (also) been sent by the current user.
|
||||
*/
|
||||
val isHighlighted: Boolean by lazy {
|
||||
senders.any { it.senderId.value == currentUserId.value }
|
||||
}
|
||||
val isHighlighted: Boolean = senders.any { it.senderId == currentUserId }
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.model
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
|
||||
@@ -53,6 +54,6 @@ fun anAggregatedReaction(
|
||||
return AggregatedReaction(
|
||||
currentUserId = userId,
|
||||
key = key,
|
||||
senders = senders
|
||||
senders = senders.toImmutableList()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
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.user.MatrixUser
|
||||
import java.util.Date
|
||||
|
||||
@Immutable
|
||||
data class AggregatedReactionSender(
|
||||
val senderId: UserId,
|
||||
val timestamp: Date,
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
<string name="screen_room_attachment_source_poll">"გამოკითხვა"</string>
|
||||
<string name="screen_room_attachment_text_formatting">"ტექსტის ფორმატირება"</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_title">"თქვენ მარტო ხართ ამ ჩატში"</string>
|
||||
<string name="screen_room_mentions_at_room_subtitle">"მთელი ოთახისათვის შეტყობინება"</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_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.matrix.api.core.EventId
|
||||
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.permalink.PermalinkParser
|
||||
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.consumeItemsUntilTimeout
|
||||
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.value
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
@@ -136,7 +138,7 @@ class MessagesPresenterTest {
|
||||
assertThat(initialState.roomAvatar)
|
||||
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
|
||||
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
|
||||
assertThat(initialState.userHasPermissionToRedactOwn).isFalse()
|
||||
assertThat(initialState.userHasPermissionToRedactOwn).isTrue()
|
||||
assertThat(initialState.hasNetworkConnection).isTrue()
|
||||
assertThat(initialState.snackbarMessage).isNull()
|
||||
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
|
||||
@@ -147,7 +149,13 @@ class MessagesPresenterTest {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
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()
|
||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -161,8 +169,13 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanUserJoinCall(Result.success(false))
|
||||
val room = FakeMatrixRoom(
|
||||
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))
|
||||
}
|
||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||
@@ -183,7 +196,14 @@ class MessagesPresenterTest {
|
||||
val timeline = FakeTimeline().apply {
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -213,7 +233,14 @@ class MessagesPresenterTest {
|
||||
val timeline = FakeTimeline().apply {
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -266,6 +293,11 @@ class MessagesPresenterTest {
|
||||
val event = aMessageEvent()
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
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(
|
||||
clipboardHelper = clipboardHelper,
|
||||
@@ -448,7 +480,14 @@ class MessagesPresenterTest {
|
||||
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||
|
||||
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) }
|
||||
liveTimeline.redactEventLambda = redactEventLambda
|
||||
@@ -513,7 +552,16 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -539,7 +587,16 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -554,7 +611,16 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -569,7 +635,16 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
MatrixRoomMembersState.Ready(
|
||||
persistentListOf(
|
||||
@@ -589,13 +664,22 @@ class MessagesPresenterTest {
|
||||
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
||||
val newState = awaitItem()
|
||||
assertThat(newState.inviteProgress.isSuccess()).isTrue()
|
||||
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2)
|
||||
inviteUserResult.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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(
|
||||
MatrixRoomMembersState.Error(
|
||||
failure = Throwable(),
|
||||
@@ -618,13 +702,20 @@ class MessagesPresenterTest {
|
||||
assertThat(loadingState.inviteProgress.isLoading()).isTrue()
|
||||
val newState = awaitItem()
|
||||
assertThat(newState.inviteProgress.isSuccess()).isTrue()
|
||||
assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2)
|
||||
inviteUserResult.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -642,7 +733,15 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
MatrixRoomMembersState.Ready(
|
||||
persistentListOf(
|
||||
@@ -651,7 +750,6 @@ class MessagesPresenterTest {
|
||||
)
|
||||
)
|
||||
)
|
||||
room.givenInviteUserResult(Result.failure(Throwable("Oops!")))
|
||||
val presenter = createMessagesPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -671,8 +769,19 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - permission to post`() = runTest {
|
||||
val matrixRoom = FakeMatrixRoom()
|
||||
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(true))
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -684,8 +793,19 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - no permission to post`() = runTest {
|
||||
val matrixRoom = FakeMatrixRoom()
|
||||
matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(false))
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -700,7 +820,13 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -714,7 +840,13 @@ class MessagesPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -754,7 +886,13 @@ class MessagesPresenterTest {
|
||||
|
||||
private fun TestScope.createMessagesPresenter(
|
||||
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 = ""))
|
||||
},
|
||||
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.AttachmentsPreviewPresenter
|
||||
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.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
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.test.viewer.aLocalMedia
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -49,13 +52,16 @@ class AttachmentsPreviewPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - send media success scenario`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
room.givenProgressCallbackValues(
|
||||
listOf(
|
||||
val sendMediaResult = lambdaRecorder<ProgressCallback?, Result<FakeMediaUploadHandler>> {
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
progressCallbackValues = listOf(
|
||||
Pair(0, 10),
|
||||
Pair(5, 10),
|
||||
Pair(10, 10)
|
||||
)
|
||||
),
|
||||
sendMediaResult = sendMediaResult,
|
||||
)
|
||||
val presenter = createAttachmentsPreviewPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -70,15 +76,19 @@ class AttachmentsPreviewPresenterTest {
|
||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f))
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
|
||||
assertThat(room.sendMediaCount).isEqualTo(1)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send media failure scenario`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -90,7 +100,7 @@ class AttachmentsPreviewPresenterTest {
|
||||
assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||
val failureState = awaitItem()
|
||||
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
|
||||
assertThat(room.sendMediaCount).isEqualTo(0)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState)
|
||||
val clearedState = awaitItem()
|
||||
assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||
|
||||
@@ -22,11 +22,14 @@ import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
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.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -81,7 +84,12 @@ class ReportMessagePresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -92,13 +100,18 @@ class ReportMessagePresenterTest {
|
||||
initialState.eventSink(ReportMessageEvents.Report)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
||||
reportContentResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -107,15 +120,18 @@ class ReportMessagePresenterTest {
|
||||
initialState.eventSink(ReportMessageEvents.Report)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Success::class.java)
|
||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
||||
reportContentResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `presenter - handle failed report`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenReportContentResult(Result.failure(Exception("Failed to report content")))
|
||||
val reportContentResult = lambdaRecorder<EventId, String, UserId?, Result<Unit>> { _, _, _ ->
|
||||
Result.failure(Exception("Failed to report content"))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
reportContentResult = reportContentResult
|
||||
)
|
||||
val presenter = createReportMessagePresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -125,7 +141,7 @@ class ReportMessagePresenterTest {
|
||||
assertThat(awaitItem().result).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
val resultState = awaitItem()
|
||||
assertThat(resultState.result).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
assertThat(room.reportedContentCount).isEqualTo(1)
|
||||
reportContentResult.assertions().isCalledOnce()
|
||||
|
||||
resultState.eventSink(ReportMessageEvents.ClearError)
|
||||
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.test.FakeFeatureFlagService
|
||||
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.TransactionId
|
||||
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.draft.ComposerDraft
|
||||
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.test.ANOTHER_MESSAGE
|
||||
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_4
|
||||
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.FakePermalinkParser
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
@@ -297,7 +300,13 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
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) {
|
||||
val state = presenter.present()
|
||||
remember(state, state.textEditorState.messageHtml()) { state }
|
||||
@@ -324,7 +333,14 @@ class MessageComposerPresenterTest {
|
||||
@Test
|
||||
fun `present - send message with plain text enabled`() = runTest {
|
||||
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) {
|
||||
val state = presenter.present()
|
||||
val messageMarkdown = state.textEditorState.messageMarkdown(permalinkBuilder)
|
||||
@@ -358,7 +374,10 @@ class MessageComposerPresenterTest {
|
||||
val timeline = FakeTimeline().apply {
|
||||
this.editMessageLambda = editMessageLambda
|
||||
}
|
||||
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline)
|
||||
val fakeMatrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = timeline,
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
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
|
||||
fun `present - edit not sent message`() = runTest {
|
||||
val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List<Mention> ->
|
||||
@@ -407,7 +487,10 @@ class MessageComposerPresenterTest {
|
||||
val timeline = FakeTimeline().apply {
|
||||
this.editMessageLambda = editMessageLambda
|
||||
}
|
||||
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline)
|
||||
val fakeMatrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = timeline,
|
||||
typingNoticeResult = { Result.success(Unit) },
|
||||
)
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
@@ -456,7 +539,10 @@ class MessageComposerPresenterTest {
|
||||
val timeline = FakeTimeline().apply {
|
||||
this.replyMessageLambda = replyMessageLambda
|
||||
}
|
||||
val fakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline)
|
||||
val fakeMatrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = timeline,
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
fakeMatrixRoom,
|
||||
@@ -524,7 +610,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Pick image from gallery`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(this, room = room)
|
||||
pickerProvider.givenMimeType(MimeTypes.Images)
|
||||
mediaPreProcessor.givenResult(
|
||||
@@ -557,7 +645,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Pick video from gallery`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(this, room = room)
|
||||
pickerProvider.givenMimeType(MimeTypes.Videos)
|
||||
mediaPreProcessor.givenResult(
|
||||
@@ -607,13 +697,17 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Pick file from storage`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
room.givenProgressCallbackValues(
|
||||
listOf(
|
||||
val sendMediaResult = lambdaRecorder { _: ProgressCallback? ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
progressCallbackValues = listOf(
|
||||
Pair(0, 10),
|
||||
Pair(5, 10),
|
||||
Pair(10, 10)
|
||||
)
|
||||
),
|
||||
sendMediaResult = sendMediaResult,
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(this, room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -629,13 +723,15 @@ class MessageComposerPresenterTest {
|
||||
assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f))
|
||||
val sentState = awaitItem()
|
||||
assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None)
|
||||
assertThat(room.sendMediaCount).isEqualTo(1)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - create poll`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(this, room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -652,7 +748,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - share location`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(this, room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -669,7 +767,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Take photo`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
@@ -689,7 +789,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Take photo with permission request`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val permissionPresenter = FakePermissionsPresenter()
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
@@ -714,7 +816,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Record video`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() }
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
@@ -734,7 +838,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Record video with permission request`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val room = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val permissionPresenter = FakePermissionsPresenter()
|
||||
val presenter = createPresenter(
|
||||
this,
|
||||
@@ -759,9 +865,10 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Uploading media failure can be recovered from`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenSendMediaResult(Result.failure(Exception()))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
sendMediaResult = { Result.failure(Exception()) },
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
)
|
||||
val presenter = createPresenter(this, room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -842,15 +949,17 @@ class MessageComposerPresenterTest {
|
||||
val invitedUser = aRoomMember(userId = A_USER_ID_3, membership = RoomMembershipState.INVITE)
|
||||
val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN)
|
||||
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
|
||||
var canUserTriggerRoomNotificationResult = true
|
||||
val room = FakeMatrixRoom(
|
||||
isDirect = false,
|
||||
canUserTriggerRoomNotificationResult = { Result.success(canUserTriggerRoomNotificationResult) },
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
).apply {
|
||||
givenRoomMembersState(
|
||||
MatrixRoomMembersState.Ready(
|
||||
persistentListOf(currentUser, invitedUser, bob, david),
|
||||
)
|
||||
)
|
||||
givenCanTriggerRoomNotification(Result.success(true))
|
||||
}
|
||||
val flagsService = FakeFeatureFlagService(
|
||||
mapOf(
|
||||
@@ -890,13 +999,10 @@ class MessageComposerPresenterTest {
|
||||
assertThat(awaitItem().memberSuggestions).isEmpty()
|
||||
|
||||
// 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, "")))
|
||||
assertThat(awaitItem().memberSuggestions)
|
||||
.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,
|
||||
activeMemberCount = 2,
|
||||
isEncrypted = true,
|
||||
canUserTriggerRoomNotificationResult = { Result.success(true) },
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
).apply {
|
||||
givenRoomMembersState(
|
||||
MatrixRoomMembersState.Ready(
|
||||
persistentListOf(currentUser, invitedUser, bob, david),
|
||||
)
|
||||
)
|
||||
givenCanTriggerRoomNotification(Result.success(true))
|
||||
}
|
||||
val flagsService = FakeFeatureFlagService(
|
||||
mapOf(
|
||||
@@ -973,7 +1080,14 @@ class MessageComposerPresenterTest {
|
||||
this.replyMessageLambda = replyMessageLambda
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -993,7 +1107,8 @@ class MessageComposerPresenterTest {
|
||||
|
||||
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
|
||||
initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode()))
|
||||
@@ -1049,22 +1164,32 @@ class MessageComposerPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(room.typingRecord).isEmpty()
|
||||
typingNoticeResult.assertions().isNeverCalled()
|
||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
||||
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
|
||||
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(
|
||||
isSendTypingNotificationsEnabled = false
|
||||
)
|
||||
@@ -1073,10 +1198,10 @@ class MessageComposerPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(room.typingRecord).isEmpty()
|
||||
typingNoticeResult.assertions().isNeverCalled()
|
||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
||||
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
|
||||
assertThat(room.typingRecord).isEmpty()
|
||||
typingNoticeResult.assertions().isNeverCalled()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1215,7 +1340,10 @@ class MessageComposerPresenterTest {
|
||||
val timeline = FakeTimeline().apply {
|
||||
this.loadReplyDetailsLambda = loadReplyDetailsLambda
|
||||
}
|
||||
val room = FakeMatrixRoom(liveTimeline = timeline)
|
||||
val room = FakeMatrixRoom(
|
||||
liveTimeline = timeline,
|
||||
typingNoticeResult = { Result.success(Unit) },
|
||||
)
|
||||
val permalinkBuilder = FakePermalinkBuilder()
|
||||
val presenter = createPresenter(
|
||||
room = room,
|
||||
@@ -1352,7 +1480,9 @@ class MessageComposerPresenterTest {
|
||||
|
||||
private fun createPresenter(
|
||||
coroutineScope: CoroutineScope,
|
||||
room: MatrixRoom = FakeMatrixRoom(),
|
||||
room: MatrixRoom = FakeMatrixRoom(
|
||||
typingNoticeResult = { Result.success(Unit) }
|
||||
),
|
||||
pickerProvider: PickerProvider = this.pickerProvider,
|
||||
featureFlagService: FeatureFlagService = this.featureFlagService,
|
||||
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.timeline.FakeTimeline
|
||||
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 kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
@@ -38,9 +39,9 @@ class TimelineControllerTest {
|
||||
val liveTimeline = FakeTimeline(name = "live")
|
||||
val detachedTimeline = FakeTimeline(name = "detached")
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = liveTimeline
|
||||
liveTimeline = liveTimeline,
|
||||
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||
)
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
||||
val sut = TimelineController(matrixRoom)
|
||||
|
||||
sut.activeTimelineFlow().test {
|
||||
@@ -68,8 +69,17 @@ class TimelineControllerTest {
|
||||
val liveTimeline = FakeTimeline(name = "live")
|
||||
val detachedTimeline1 = FakeTimeline(name = "detached 1")
|
||||
val detachedTimeline2 = FakeTimeline(name = "detached 2")
|
||||
var callNumber = 0
|
||||
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)
|
||||
|
||||
@@ -77,7 +87,6 @@ class TimelineControllerTest {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state).isEqualTo(liveTimeline)
|
||||
}
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline1))
|
||||
sut.focusOnEvent(AN_EVENT_ID)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state).isEqualTo(detachedTimeline1)
|
||||
@@ -85,7 +94,6 @@ class TimelineControllerTest {
|
||||
assertThat(detachedTimeline1.closeCounter).isEqualTo(0)
|
||||
assertThat(detachedTimeline2.closeCounter).isEqualTo(0)
|
||||
// Focus on another event should close the previous detached timeline
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline2))
|
||||
sut.focusOnEvent(AN_EVENT_ID)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state).isEqualTo(detachedTimeline2)
|
||||
@@ -117,11 +125,10 @@ class TimelineControllerTest {
|
||||
val liveTimeline = FakeTimeline(name = "live")
|
||||
val detachedTimeline = FakeTimeline(name = "detached")
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = liveTimeline
|
||||
liveTimeline = liveTimeline,
|
||||
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||
)
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
||||
val sut = TimelineController(matrixRoom)
|
||||
|
||||
sut.activeTimelineFlow().test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state).isEqualTo(liveTimeline)
|
||||
@@ -168,9 +175,9 @@ class TimelineControllerTest {
|
||||
sendMessageLambda = lambdaForDetached
|
||||
}
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = liveTimeline
|
||||
liveTimeline = liveTimeline,
|
||||
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||
)
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
||||
val sut = TimelineController(matrixRoom)
|
||||
sut.activeTimelineFlow().test {
|
||||
sut.focusOnEvent(AN_EVENT_ID)
|
||||
@@ -193,9 +200,9 @@ class TimelineControllerTest {
|
||||
val liveTimeline = FakeTimeline(name = "live")
|
||||
val detachedTimeline = FakeTimeline(name = "detached")
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = liveTimeline
|
||||
liveTimeline = liveTimeline,
|
||||
timelineFocusedOnEventResult = { Result.success(detachedTimeline) }
|
||||
)
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
||||
val sut = TimelineController(matrixRoom)
|
||||
|
||||
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 presenter = createTimelinePresenter(
|
||||
timeline = timeline,
|
||||
@@ -482,9 +485,9 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||
)
|
||||
val room = FakeMatrixRoom(
|
||||
liveTimeline = liveTimeline,
|
||||
).apply {
|
||||
givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
||||
}
|
||||
timelineFocusedOnEventResult = { Result.success(detachedTimeline) },
|
||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||
)
|
||||
val presenter = createTimelinePresenter(
|
||||
room = room,
|
||||
)
|
||||
@@ -529,6 +532,7 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||
)
|
||||
)
|
||||
),
|
||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||
),
|
||||
timelineItemIndexer = timelineItemIndexer,
|
||||
)
|
||||
@@ -551,9 +555,9 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||
liveTimeline = FakeTimeline(
|
||||
timelineItems = flowOf(emptyList()),
|
||||
),
|
||||
).apply {
|
||||
givenTimelineFocusedOnEventResult(Result.failure(Throwable("An error")))
|
||||
},
|
||||
timelineFocusedOnEventResult = { Result.failure(Throwable("An error")) },
|
||||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -626,7 +633,10 @@ private const val FAKE_UNIQUE_ID_2 = "FAKE_UNIQUE_ID_2"
|
||||
|
||||
private fun TestScope.createTimelinePresenter(
|
||||
timeline: Timeline = FakeTimeline(),
|
||||
room: FakeMatrixRoom = FakeMatrixRoom(liveTimeline = timeline),
|
||||
room: FakeMatrixRoom = FakeMatrixRoom(
|
||||
liveTimeline = timeline,
|
||||
canUserSendMessageResult = { _, _ -> Result.success(true) }
|
||||
),
|
||||
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(),
|
||||
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
|
||||
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||
|
||||
@@ -20,9 +20,11 @@ import android.net.Uri
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.SpannedString
|
||||
import android.text.style.URLSpan
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.core.text.toSpannable
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.location.api.Location
|
||||
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.toImmutableList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
@@ -195,7 +198,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||
inSpans(URLSpan("https://matrix.org")) {
|
||||
append("and manually added link")
|
||||
}
|
||||
}
|
||||
}.toSpannable()
|
||||
val sut = createTimelineItemContentMessageFactory(
|
||||
htmlConverterTransform = { expected }
|
||||
)
|
||||
@@ -610,7 +613,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||
senderDisambiguatedDisplayName = "Bob",
|
||||
eventId = AN_EVENT_ID,
|
||||
)
|
||||
assertThat((result as TimelineItemNoticeContent).formattedBody).isEqualTo("formatted")
|
||||
(result as TimelineItemNoticeContent).formattedBody.assertSpannedEquals(SpannedString("formatted"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -644,7 +647,8 @@ class TimelineItemContentMessageFactoryTest {
|
||||
senderDisambiguatedDisplayName = "Bob",
|
||||
eventId = AN_EVENT_ID,
|
||||
)
|
||||
assertThat((result as TimelineItemEmoteContent).formattedBody).isEqualTo(SpannableString("* Bob formatted"))
|
||||
|
||||
(result as TimelineItemEmoteContent).formattedBody.assertSpannedEquals(SpannableString("* Bob formatted"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -654,7 +658,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||
inSpans(URLSpan("https://www.example.org")) {
|
||||
append("me@matrix.org")
|
||||
}
|
||||
}
|
||||
}.toSpannable()
|
||||
val sut = createTimelineItemContentMessageFactory(
|
||||
htmlConverterTransform = { expectedSpanned },
|
||||
permalinkParser = FakePermalinkParser { PermalinkData.FallbackLink(Uri.EMPTY) }
|
||||
@@ -669,7 +673,59 @@ class TimelineItemContentMessageFactoryTest {
|
||||
senderDisambiguatedDisplayName = "Bob",
|
||||
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(
|
||||
@@ -718,3 +774,40 @@ class TimelineItemContentMessageFactoryTest {
|
||||
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 io.element.android.features.messages.impl.voicemessages.VoiceMessageException
|
||||
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.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
|
||||
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.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -63,7 +66,10 @@ class VoiceMessageComposerPresenterTest {
|
||||
recordingDuration = RECORDING_DURATION
|
||||
)
|
||||
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 mediaSender = MediaSender(mediaPreProcessor, matrixRoom)
|
||||
private val messageComposerContext = FakeMessageComposerContext()
|
||||
@@ -295,7 +301,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||
|
||||
testPauseAndDestroy(finalState)
|
||||
@@ -346,7 +352,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||
|
||||
testPauseAndDestroy(finalState)
|
||||
@@ -369,7 +375,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||
|
||||
testPauseAndDestroy(finalState)
|
||||
@@ -393,7 +399,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState(isSending = true))
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
||||
sendMediaResult.assertions().isNeverCalled()
|
||||
assertThat(analyticsService.trackedErrors).hasSize(0)
|
||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0)
|
||||
|
||||
@@ -418,13 +424,13 @@ class VoiceMessageComposerPresenterTest {
|
||||
|
||||
ensureAllEventsConsumed()
|
||||
assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState())
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
||||
sendMediaResult.assertions().isNeverCalled()
|
||||
|
||||
mediaPreProcessor.givenAudioResult()
|
||||
previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(1)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1)
|
||||
|
||||
testPauseAndDestroy(finalState)
|
||||
@@ -461,7 +467,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
assertThat(showSendFailureDialog).isFalse()
|
||||
}
|
||||
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
||||
sendMediaResult.assertions().isNeverCalled()
|
||||
testPauseAndDestroy(finalState)
|
||||
}
|
||||
}
|
||||
@@ -477,7 +483,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
initialState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
||||
|
||||
assertThat(initialState.voiceMessageState).isEqualTo(VoiceMessageState.Idle)
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
||||
sendMediaResult.assertions().isNeverCalled()
|
||||
assertThat(analyticsService.trackedErrors).hasSize(1)
|
||||
voiceRecorder.assertCalls(started = 0)
|
||||
|
||||
@@ -496,7 +502,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start))
|
||||
|
||||
assertThat(matrixRoom.sendMediaCount).isEqualTo(0)
|
||||
sendMediaResult.assertions().isNeverCalled()
|
||||
assertThat(analyticsService.trackedErrors).containsExactly(
|
||||
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_headline">"ხმების დამალვა"</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_hint">"რას ეხება გამოკითხვა?"</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_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>
|
||||
|
||||
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.test.AN_EVENT_ID
|
||||
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.LiveTimelineProvider
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
@@ -51,9 +50,9 @@ import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class) class CreatePollPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class CreatePollPresenterTest {
|
||||
@get:Rule val warmUpRule = WarmUpRule()
|
||||
|
||||
private val pollEventId = AN_EVENT_ID
|
||||
private var navUpInvocationsCount = 0
|
||||
@@ -128,7 +127,13 @@ import org.junit.Test
|
||||
|
||||
@Test
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -139,15 +144,13 @@ import org.junit.Test
|
||||
skipItems(3)
|
||||
initial.eventSink(CreatePollEvents.Save)
|
||||
delay(1) // Wait for the coroutine to finish
|
||||
assertThat(fakeMatrixRoom.createPollInvocations.size).isEqualTo(1)
|
||||
assertThat(fakeMatrixRoom.createPollInvocations.last()).isEqualTo(
|
||||
SavePollInvocation(
|
||||
question = "A question?",
|
||||
answers = listOf("Answer 1", "Answer 2"),
|
||||
maxSelections = 1,
|
||||
pollKind = PollKind.Disclosed
|
||||
createPollResult.assertions().isCalledOnce()
|
||||
.with(
|
||||
value("A question?"),
|
||||
value(listOf("Answer 1", "Answer 2")),
|
||||
value(1),
|
||||
value(PollKind.Disclosed),
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(2)
|
||||
assertThat(fakeAnalyticsService.capturedEvents[0]).isEqualTo(
|
||||
Composer(
|
||||
@@ -170,8 +173,15 @@ import org.junit.Test
|
||||
@Test
|
||||
fun `when poll creation fails, error is tracked`() = runTest {
|
||||
val error = Exception("cause")
|
||||
fakeMatrixRoom.givenCreatePollResult(Result.failure(error))
|
||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.NewPoll)
|
||||
val createPollResult = lambdaRecorder<String, List<String>, Int, PollKind, Result<Unit>> { _, _, _, _ ->
|
||||
Result.failure(error)
|
||||
}
|
||||
val presenter = createCreatePollPresenter(
|
||||
room = FakeMatrixRoom(
|
||||
createPollResult = createPollResult
|
||||
),
|
||||
mode = CreatePollMode.NewPoll,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -180,7 +190,7 @@ import org.junit.Test
|
||||
awaitItem().eventSink(CreatePollEvents.SetAnswer(1, "Answer 2"))
|
||||
awaitItem().eventSink(CreatePollEvents.Save)
|
||||
delay(1) // Wait for the coroutine to finish
|
||||
assertThat(fakeMatrixRoom.createPollInvocations).hasSize(1)
|
||||
createPollResult.assertions().isCalledOnce()
|
||||
assertThat(fakeAnalyticsService.capturedEvents).isEmpty()
|
||||
assertThat(fakeAnalyticsService.trackedErrors).hasSize(1)
|
||||
assertThat(fakeAnalyticsService.trackedErrors).containsExactly(
|
||||
@@ -252,14 +262,22 @@ import org.junit.Test
|
||||
@Test
|
||||
fun `when edit poll fails, error is tracked`() = runTest {
|
||||
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 ->
|
||||
Result.failure<Unit>(error)
|
||||
}
|
||||
timeline.apply {
|
||||
this.editPollLambda = editPollLambda
|
||||
}
|
||||
fakeMatrixRoom.givenEditPollResult(Result.failure(error))
|
||||
val presenter = createCreatePollPresenter(mode = CreatePollMode.EditPoll(pollEventId))
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
<string name="screen_advanced_settings_share_presence">"Κοινή χρήση παρουσίας"</string>
|
||||
<string name="screen_advanced_settings_share_presence_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_description">"Θα μπορείς να δεις ξανά όλα τα μηνύματα του."</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_placeholder">"Το εμφανιζόμενο όνομά σου"</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_mode_all">"Όλα"</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_system_notifications_action_required">"Για να λαμβάνεις ειδοποιήσεις, άλλαξε το %1$s ."</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_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_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_blocked_users_empty">"Non hai utenti bloccati"</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_validation_error">"არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."</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_description">"თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას."</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_failed_fixing_configuration">"კონფიგურაცია არ გამოსწორებულა, გთხოვთ, კვლავ სცადოთ."</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_mode_all">"ყველა"</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_polls_history_title">"Δημοσκοπήσεις"</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_everyone">"Όλοι"</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_topic_title">"Θέμα"</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_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">
|
||||
<item quantity="one">"%1$d άτομο"</item>
|
||||
<item quantity="other">"%1$d άτομα"</item>
|
||||
</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_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_title">"Αφαίρεση μέλους και απαγόρευση συμμετοχής στο μέλλον;"</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_title">"Άρση αποκλεισμού χρήστη"</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_members">"Μέλη"</string>
|
||||
@@ -77,6 +84,7 @@
|
||||
<string name="screen_room_member_list_role_administrator">"Διαχειριστής"</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_unbanning_user">"Άρση αποκλεισμού %1$s"</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_custom_settings_title">"Ειδοποιήσε με σε αυτήν τη συνομιλία για"</string>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?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">"შეტყობინებების პარამეტრის განახლებისას მოხდა შეცდომა."</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_details_add_topic_title">"თემის დამატება"</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_restoring_default">"ნაგულისხმევი რეჟიმის აღდგენა ვერ მოხერხდა, გთხოვთ, სცადოთ ხელახლა."</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_mentions_and_keywords">"მხოლოდ ხსენებები და საკვანძო სიტყვები"</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
|
||||
|
||||
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_NAME
|
||||
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.aRoomInfo
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
fun aMatrixRoom(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
@@ -34,6 +38,16 @@ fun aMatrixRoom(
|
||||
isDirect: Boolean = false,
|
||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
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(
|
||||
roomId = roomId,
|
||||
displayName = displayName,
|
||||
@@ -42,7 +56,17 @@ fun aMatrixRoom(
|
||||
isEncrypted = isEncrypted,
|
||||
isPublic = isPublic,
|
||||
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 {
|
||||
if (emitRoomInfo) {
|
||||
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.WarmUpRule
|
||||
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.withFakeLifecycleOwner
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -110,7 +113,11 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
@@ -128,7 +135,11 @@ class RoomDetailsPresenterTest {
|
||||
@Test
|
||||
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 room = aMatrixRoom().apply {
|
||||
val room = aMatrixRoom(
|
||||
canInviteResult = { Result.success(true) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
).apply {
|
||||
givenRoomInfo(roomInfo)
|
||||
}
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
@@ -145,7 +156,12 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
@@ -162,6 +178,16 @@ class RoomDetailsPresenterTest {
|
||||
val room = aMatrixRoom(
|
||||
isEncrypted = 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 {
|
||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||
@@ -181,9 +207,11 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state when user can invite others to room`() = runTest {
|
||||
val room = aMatrixRoom().apply {
|
||||
givenCanInviteResult(Result.success(true))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
canInviteResult = { Result.success(true) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
)
|
||||
val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers())
|
||||
presenter.test {
|
||||
// Initially false
|
||||
@@ -197,9 +225,11 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state when user can not invite others to room`() = runTest {
|
||||
val room = aMatrixRoom().apply {
|
||||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
canInviteResult = { Result.success(false) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
)
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.test {
|
||||
assertThat(awaitItem().canInvite).isFalse()
|
||||
@@ -210,9 +240,11 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state when canInvite errors`() = runTest {
|
||||
val room = aMatrixRoom().apply {
|
||||
givenCanInviteResult(Result.failure(Throwable("Whoops")))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
canInviteResult = { Result.failure(Throwable("Whoops")) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
)
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.test {
|
||||
assertThat(awaitItem().canInvite).isFalse()
|
||||
@@ -223,12 +255,18 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state when user can edit one attribute`() = runTest {
|
||||
val room = aMatrixRoom().apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp")))
|
||||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
when (stateEventType) {
|
||||
StateEventType.ROOM_TOPIC -> Result.success(true)
|
||||
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)
|
||||
presenter.test {
|
||||
// Initially false
|
||||
@@ -247,14 +285,26 @@ class RoomDetailsPresenterTest {
|
||||
val room = aMatrixRoom(
|
||||
isEncrypted = 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 {
|
||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||
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)
|
||||
presenter.test {
|
||||
@@ -278,12 +328,28 @@ class RoomDetailsPresenterTest {
|
||||
isEncrypted = true,
|
||||
isDirect = true,
|
||||
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 {
|
||||
val roomMembers = persistentListOf(myRoomMember, otherRoomMember)
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||
}
|
||||
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
@@ -297,12 +363,20 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state when user can edit all attributes`() = runTest {
|
||||
val room = aMatrixRoom().apply {
|
||||
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 room = aMatrixRoom(
|
||||
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) },
|
||||
)
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.test {
|
||||
// Initially false
|
||||
@@ -316,12 +390,20 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state when user can edit no attributes`() = runTest {
|
||||
val room = aMatrixRoom().apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false))
|
||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
|
||||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
when (stateEventType) {
|
||||
StateEventType.ROOM_TOPIC -> 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)
|
||||
presenter.test {
|
||||
// Initially false, and no further events
|
||||
@@ -333,11 +415,21 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - topic state is hidden when no topic and user has no permission`() = runTest {
|
||||
val room = aMatrixRoom(topic = null).apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false))
|
||||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
|
||||
val room = aMatrixRoom(
|
||||
topic = null,
|
||||
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)
|
||||
presenter.test {
|
||||
// The initial state is "hidden" and no further state changes happen
|
||||
@@ -349,12 +441,23 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest {
|
||||
val room = aMatrixRoom(topic = null).apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||
givenCanInviteResult(Result.success(false))
|
||||
val room = aMatrixRoom(
|
||||
topic = null,
|
||||
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))
|
||||
}
|
||||
|
||||
val presenter = createRoomDetailsPresenter(room)
|
||||
presenter.test {
|
||||
// Ignore the initial state
|
||||
@@ -370,7 +473,11 @@ class RoomDetailsPresenterTest {
|
||||
@Test
|
||||
fun `present - leave room event is passed on to leave room presenter`() = runTest {
|
||||
val leaveRoomPresenter = FakeLeaveRoomPresenter()
|
||||
val room = aMatrixRoom()
|
||||
val room = aMatrixRoom(
|
||||
canInviteResult = { Result.success(true) },
|
||||
canUserJoinCallResult = { Result.success(true) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) },
|
||||
)
|
||||
val presenter = createRoomDetailsPresenter(
|
||||
room = room,
|
||||
leaveRoomPresenter = leaveRoomPresenter,
|
||||
@@ -379,7 +486,11 @@ class RoomDetailsPresenterTest {
|
||||
presenter.test {
|
||||
awaitItem().eventSink(RoomDetailsEvent.LeaveRoom)
|
||||
|
||||
assertThat(leaveRoomPresenter.events).contains(LeaveRoomEvent.ShowConfirmation(room.roomId))
|
||||
assertThat(leaveRoomPresenter.events).contains(
|
||||
LeaveRoomEvent.ShowConfirmation(
|
||||
room.roomId
|
||||
)
|
||||
)
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
@@ -389,33 +500,54 @@ class RoomDetailsPresenterTest {
|
||||
fun `present - notification mode changes`() = runTest {
|
||||
val leaveRoomPresenter = FakeLeaveRoomPresenter()
|
||||
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(
|
||||
room = room,
|
||||
leaveRoomPresenter = leaveRoomPresenter,
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
)
|
||||
presenter.test {
|
||||
notificationSettingsService.setRoomNotificationMode(room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
notificationSettingsService.setRoomNotificationMode(
|
||||
room.roomId,
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
)
|
||||
val updatedState = consumeItemsUntilPredicate {
|
||||
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
}.last()
|
||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - mute room notifications`() = runTest {
|
||||
val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
||||
val presenter = createRoomDetailsPresenter(room = room, notificationSettingsService = notificationSettingsService)
|
||||
val notificationSettingsService =
|
||||
FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
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 {
|
||||
awaitItem().eventSink(RoomDetailsEvent.MuteNotification)
|
||||
val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) {
|
||||
it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE
|
||||
}.last()
|
||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MUTE)
|
||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
|
||||
RoomNotificationMode.MUTE
|
||||
)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
@@ -426,29 +558,50 @@ class RoomDetailsPresenterTest {
|
||||
initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
|
||||
)
|
||||
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
|
||||
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 {
|
||||
awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification)
|
||||
val updatedState = consumeItemsUntilPredicate {
|
||||
it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES
|
||||
}.last()
|
||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
|
||||
assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(
|
||||
RoomNotificationMode.ALL_MESSAGES
|
||||
)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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 presenter = createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
|
||||
val presenter =
|
||||
createRoomDetailsPresenter(room = room, analyticsService = analyticsService)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEvent.SetFavorite(true))
|
||||
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true))
|
||||
setIsFavoriteResult.assertions().isCalledOnce().with(value(true))
|
||||
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(
|
||||
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle),
|
||||
Interaction(name = Interaction.Name.MobileRoomFavouriteToggle)
|
||||
@@ -459,7 +612,11 @@ class RoomDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
presenter.test {
|
||||
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.FakePermissionsPresenterFactory
|
||||
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.mockk
|
||||
import io.mockk.mockkStatic
|
||||
@@ -98,6 +101,7 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = A_ROOM_NAME,
|
||||
rawName = A_ROOM_RAW_NAME,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -120,11 +124,17 @@ class RoomDetailsEditPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - sets canChangeName if user has permission`() = runTest {
|
||||
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
|
||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false))
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -144,11 +154,17 @@ class RoomDetailsEditPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - sets canChangeAvatar if user has permission`() = runTest {
|
||||
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops")))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -168,11 +184,17 @@ class RoomDetailsEditPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - sets canChangeTopic if user has permission`() = runTest {
|
||||
val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply {
|
||||
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false))
|
||||
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Oops")))
|
||||
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
canSendStateResult = { _, stateEventType ->
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -197,6 +219,7 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -240,6 +263,7 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
@@ -262,6 +286,7 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||
val fakePermissionsPresenter = FakePermissionsPresenter()
|
||||
@@ -298,6 +323,7 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(roomAvatarUri)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
@@ -346,6 +372,7 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "fallback",
|
||||
avatarUrl = null,
|
||||
emitRoomInfo = true,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
fakePickerProvider.givenResult(roomAvatarUri)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
@@ -389,11 +416,18 @@ class RoomDetailsEditPresenterTest {
|
||||
|
||||
@Test
|
||||
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(
|
||||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
setNameResult = setNameResult,
|
||||
setTopicResult = setTopicResult,
|
||||
removeAvatarResult = removeAvatarResult,
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -405,16 +439,20 @@ class RoomDetailsEditPresenterTest {
|
||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
skipItems(5)
|
||||
assertThat(room.newName).isEqualTo("New name")
|
||||
assertThat(room.newTopic).isEqualTo("New topic")
|
||||
assertThat(room.newAvatarData).isNull()
|
||||
assertThat(room.removedAvatar).isTrue()
|
||||
setNameResult.assertions().isCalledOnce().with(value("New name"))
|
||||
setTopicResult.assertions().isCalledOnce().with(value("New topic"))
|
||||
removeAvatarResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -423,17 +461,18 @@ class RoomDetailsEditPresenterTest {
|
||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name "))
|
||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic "))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
assertThat(room.newName).isNull()
|
||||
assertThat(room.newTopic).isNull()
|
||||
assertThat(room.newAvatarData).isNull()
|
||||
assertThat(room.removedAvatar).isFalse()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -441,17 +480,18 @@ class RoomDetailsEditPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(""))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
assertThat(room.newName).isNull()
|
||||
assertThat(room.newTopic).isNull()
|
||||
assertThat(room.newAvatarData).isNull()
|
||||
assertThat(room.removedAvatar).isFalse()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -459,17 +499,20 @@ class RoomDetailsEditPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(""))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
assertThat(room.newName).isNull()
|
||||
assertThat(room.newTopic).isNull()
|
||||
assertThat(room.newAvatarData).isNull()
|
||||
assertThat(room.removedAvatar).isFalse()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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()
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -478,17 +521,19 @@ class RoomDetailsEditPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
skipItems(3)
|
||||
assertThat(room.newName).isNull()
|
||||
assertThat(room.newTopic).isNull()
|
||||
assertThat(room.newAvatarData).isSameInstanceAs(fakeFileContents)
|
||||
assertThat(room.removedAvatar).isFalse()
|
||||
skipItems(4)
|
||||
updateAvatarResult.assertions().isCalledOnce().with(value("image/jpeg"), value(fakeFileContents))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
@@ -498,11 +543,7 @@ class RoomDetailsEditPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
skipItems(2)
|
||||
assertThat(room.newName).isNull()
|
||||
assertThat(room.newTopic).isNull()
|
||||
assertThat(room.newAvatarData).isNull()
|
||||
assertThat(room.removedAvatar).isFalse()
|
||||
skipItems(3)
|
||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
}
|
||||
}
|
||||
@@ -514,9 +555,9 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
).apply {
|
||||
givenSetNameResult(Result.failure(Throwable("!")))
|
||||
}
|
||||
setNameResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"))
|
||||
}
|
||||
|
||||
@@ -527,9 +568,9 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
).apply {
|
||||
givenSetTopicResult(Result.failure(Throwable("!")))
|
||||
}
|
||||
setTopicResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"))
|
||||
}
|
||||
|
||||
@@ -540,9 +581,9 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
).apply {
|
||||
givenRemoveAvatarResult(Result.failure(Throwable("!")))
|
||||
}
|
||||
removeAvatarResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove))
|
||||
}
|
||||
|
||||
@@ -554,18 +595,22 @@ class RoomDetailsEditPresenterTest {
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
emitRoomInfo = true,
|
||||
).apply {
|
||||
givenUpdateAvatarResult(Result.failure(Throwable("!")))
|
||||
}
|
||||
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
||||
givenPickerReturnsFile()
|
||||
val room = aMatrixRoom(topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL).apply {
|
||||
givenSetTopicResult(Result.failure(Throwable("!")))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
topic = "My topic",
|
||||
displayName = "Name",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
setTopicResult = { Result.failure(Throwable("!")) },
|
||||
canSendStateResult = { _, _ -> Result.success(true) }
|
||||
)
|
||||
val presenter = createRoomDetailsEditPresenter(room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -573,7 +618,7 @@ class RoomDetailsEditPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo"))
|
||||
initialState.eventSink(RoomDetailsEditEvents.Save)
|
||||
skipItems(2)
|
||||
skipItems(3)
|
||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges)
|
||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
|
||||
@@ -52,7 +52,10 @@ class RoomMemberListPresenterTest {
|
||||
|
||||
@Test
|
||||
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
|
||||
givenRoomInfo(aRoomInfo(joinedMembersCount = 2))
|
||||
}
|
||||
@@ -78,7 +81,12 @@ class RoomMemberListPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `open search`() = runTest {
|
||||
val presenter = createPresenter()
|
||||
val presenter = createPresenter(
|
||||
matrixRoom = FakeMatrixRoom(
|
||||
updateMembersResult = { Result.success(Unit) },
|
||||
canInviteResult = { Result.success(true) }
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -93,7 +101,12 @@ class RoomMemberListPresenterTest {
|
||||
|
||||
@Test
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -112,7 +125,12 @@ class RoomMemberListPresenterTest {
|
||||
|
||||
@Test
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -134,9 +152,10 @@ class RoomMemberListPresenterTest {
|
||||
@Test
|
||||
fun `present - asynchronously sets canInvite when user has correct power level`() = runTest {
|
||||
val presenter = createPresenter(
|
||||
matrixRoom = FakeMatrixRoom().apply {
|
||||
givenCanInviteResult(Result.success(true))
|
||||
}
|
||||
matrixRoom = FakeMatrixRoom(
|
||||
canInviteResult = { Result.success(true) },
|
||||
updateMembersResult = { Result.success(Unit) }
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -150,9 +169,10 @@ class RoomMemberListPresenterTest {
|
||||
@Test
|
||||
fun `present - asynchronously sets canInvite when user does not have correct power level`() = runTest {
|
||||
val presenter = createPresenter(
|
||||
matrixRoom = FakeMatrixRoom().apply {
|
||||
givenCanInviteResult(Result.success(false))
|
||||
}
|
||||
matrixRoom = FakeMatrixRoom(
|
||||
canInviteResult = { Result.success(false) },
|
||||
updateMembersResult = { Result.success(Unit) }
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -166,9 +186,10 @@ class RoomMemberListPresenterTest {
|
||||
@Test
|
||||
fun `present - asynchronously sets canInvite when power level check fails`() = runTest {
|
||||
val presenter = createPresenter(
|
||||
matrixRoom = FakeMatrixRoom().apply {
|
||||
givenCanInviteResult(Result.failure(Throwable("Eek")))
|
||||
}
|
||||
matrixRoom = FakeMatrixRoom(
|
||||
canInviteResult = { Result.failure(Throwable("Eek")) },
|
||||
updateMembersResult = { Result.success(Unit) }
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -183,7 +204,14 @@ class RoomMemberListPresenterTest {
|
||||
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
|
||||
val navigator = FakeRoomMemberListNavigator()
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -205,7 +233,14 @@ class RoomMemberListPresenterTest {
|
||||
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply {
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -236,10 +271,12 @@ private fun TestScope.createDataSource(
|
||||
@ExperimentalCoroutinesApi
|
||||
private fun TestScope.createPresenter(
|
||||
coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
matrixRoom: MatrixRoom = FakeMatrixRoom(),
|
||||
matrixRoom: MatrixRoom = FakeMatrixRoom(
|
||||
updateMembersResult = { Result.success(Unit) }
|
||||
),
|
||||
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
|
||||
moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(),
|
||||
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator { }
|
||||
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
|
||||
) = RoomMemberListPresenter(
|
||||
room = matrixRoom,
|
||||
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.room.MatrixRoom
|
||||
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_THROWABLE
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
@@ -53,9 +54,11 @@ class RoomMemberDetailsPresenterTest {
|
||||
@Test
|
||||
fun `present - returns the room member's data, then updates it if needed`() = runTest {
|
||||
val roomMember = aRoomMember(displayName = "Alice")
|
||||
val room = aMatrixRoom().apply {
|
||||
givenUserDisplayNameResult(Result.success("A custom name"))
|
||||
givenUserAvatarUrlResult(Result.success("A custom avatar"))
|
||||
val room = aMatrixRoom(
|
||||
userDisplayNameResult = { Result.success("A custom name") },
|
||||
userAvatarUrlResult = { Result.success("A custom avatar") },
|
||||
getUpdatedMemberResult = { Result.success(roomMember) },
|
||||
).apply {
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
||||
}
|
||||
val presenter = createRoomMemberDetailsPresenter(
|
||||
@@ -82,11 +85,14 @@ class RoomMemberDetailsPresenterTest {
|
||||
@Test
|
||||
fun `present - will recover when retrieving room member details fails`() = runTest {
|
||||
val roomMember = aRoomMember(displayName = "Alice")
|
||||
val room = aMatrixRoom().apply {
|
||||
givenUserDisplayNameResult(Result.failure(Throwable()))
|
||||
givenUserAvatarUrlResult(Result.failure(Throwable()))
|
||||
val room = aMatrixRoom(
|
||||
userDisplayNameResult = { Result.failure(Throwable()) },
|
||||
userAvatarUrlResult = { Result.failure(Throwable()) },
|
||||
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||
).apply {
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
||||
}
|
||||
|
||||
val presenter = createRoomMemberDetailsPresenter(
|
||||
room = room,
|
||||
roomMemberId = roomMember.userId
|
||||
@@ -105,9 +111,11 @@ class RoomMemberDetailsPresenterTest {
|
||||
@Test
|
||||
fun `present - will fallback to original data if the updated data is null`() = runTest {
|
||||
val roomMember = aRoomMember(displayName = "Alice")
|
||||
val room = aMatrixRoom().apply {
|
||||
givenUserDisplayNameResult(Result.success(null))
|
||||
givenUserAvatarUrlResult(Result.success(null))
|
||||
val room = aMatrixRoom(
|
||||
userDisplayNameResult = { Result.success(null) },
|
||||
userAvatarUrlResult = { Result.success(null) },
|
||||
getUpdatedMemberResult = { Result.success(roomMember) }
|
||||
).apply {
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(roomMember)))
|
||||
}
|
||||
val presenter = createRoomMemberDetailsPresenter(
|
||||
@@ -128,10 +136,11 @@ class RoomMemberDetailsPresenterTest {
|
||||
@Test
|
||||
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 room = aMatrixRoom().apply {
|
||||
givenUserDisplayNameResult(Result.failure(Exception("Not a member!")))
|
||||
givenUserAvatarUrlResult(Result.failure(Exception("Not a member!")))
|
||||
}
|
||||
val room = aMatrixRoom(
|
||||
userDisplayNameResult = { Result.failure(Exception("Not a member!")) },
|
||||
userAvatarUrlResult = { Result.failure(Exception("Not a member!")) },
|
||||
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||
)
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetProfileResult(bobProfile.userId, Result.success(bobProfile))
|
||||
}
|
||||
@@ -154,7 +163,13 @@ class RoomMemberDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -176,6 +191,11 @@ class RoomMemberDetailsPresenterTest {
|
||||
val client = FakeMatrixClient()
|
||||
val roomMember = aRoomMember()
|
||||
val presenter = createRoomMemberDetailsPresenter(
|
||||
room = aMatrixRoom(
|
||||
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
|
||||
userDisplayNameResult = { Result.success("Alice") },
|
||||
userAvatarUrlResult = { Result.success("anAvatarUrl") },
|
||||
),
|
||||
client = client,
|
||||
roomMemberId = roomMember.userId
|
||||
)
|
||||
@@ -199,13 +219,21 @@ class RoomMemberDetailsPresenterTest {
|
||||
fun `present - BlockUser with error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient()
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
|
||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||
skipItems(2)
|
||||
val errorState = awaitItem()
|
||||
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
||||
// Clear error
|
||||
@@ -218,13 +246,21 @@ class RoomMemberDetailsPresenterTest {
|
||||
fun `present - UnblockUser with error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient()
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
|
||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||
skipItems(2)
|
||||
val errorState = awaitItem()
|
||||
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
||||
// Clear error
|
||||
@@ -235,7 +271,13 @@ class RoomMemberDetailsPresenterTest {
|
||||
|
||||
@Test
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -255,7 +297,14 @@ class RoomMemberDetailsPresenterTest {
|
||||
@Test
|
||||
fun `present - start DM action complete scenario`() = runTest {
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -268,6 +317,7 @@ class RoomMemberDetailsPresenterTest {
|
||||
startDMAction.givenExecuteResult(startDMFailureResult)
|
||||
initialState.eventSink(UserProfileEvents.StartDM)
|
||||
assertThat(awaitItem().startDmActionState).isInstanceOf(AsyncAction.Loading::class.java)
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.startDmActionState).isEqualTo(startDMFailureResult)
|
||||
state.eventSink(UserProfileEvents.ClearStartDMState)
|
||||
@@ -292,8 +342,8 @@ class RoomMemberDetailsPresenterTest {
|
||||
}
|
||||
|
||||
private fun createRoomMemberDetailsPresenter(
|
||||
room: MatrixRoom,
|
||||
client: MatrixClient = FakeMatrixClient(),
|
||||
room: MatrixRoom = aMatrixRoom(),
|
||||
roomMemberId: UserId = UserId("@alice:server.org"),
|
||||
startDMAction: StartDMAction = FakeStartDMAction()
|
||||
): RoomMemberDetailsPresenter {
|
||||
|
||||
@@ -54,29 +54,34 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
|
||||
@Test
|
||||
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 {
|
||||
givenCanKickResult(Result.success(true))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
isDirect = false,
|
||||
activeMemberCount = 10,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
)
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
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 {
|
||||
givenCanBanResult(Result.success(true))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
isDirect = false,
|
||||
activeMemberCount = 10,
|
||||
canBanResult = { Result.success(true) },
|
||||
)
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -98,11 +103,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
|
||||
@Test
|
||||
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 {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
sessionId = A_USER_ID,
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L)
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -123,11 +129,11 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
@Test
|
||||
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 room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
)
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -144,11 +150,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
@Test
|
||||
fun `present - Kick removes the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
kickUserResult = { _, _ -> Result.success(Unit) },
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -171,11 +178,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
@Test
|
||||
fun `present - BanUser requires confirmation and then bans the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
banUserResult = { _, _ -> Result.success(Unit) },
|
||||
)
|
||||
val selectedMember = aVictor()
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -204,11 +212,13 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN)
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||
unBanUserResult = { _, _ -> Result.success(Unit) },
|
||||
).apply {
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember)))
|
||||
givenUserRoleResult(Result.success(RoomMember.Role.ADMIN))
|
||||
}
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
@@ -231,10 +241,11 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Reset removes the selected user and actions`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||
)
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -251,13 +262,14 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Reset resets any async actions`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenCanKickResult(Result.success(true))
|
||||
givenCanBanResult(Result.success(true))
|
||||
givenKickUserResult(Result.failure(Throwable("Eek")))
|
||||
givenBanUserResult(Result.failure(Throwable("Eek")))
|
||||
givenUnbanUserResult(Result.failure(Throwable("Eek")))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
canKickResult = { Result.success(true) },
|
||||
canBanResult = { Result.success(true) },
|
||||
kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||
banUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||
unBanUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||
)
|
||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
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.matrix.api.room.RoomMember
|
||||
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.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -67,7 +68,12 @@ class RolesAndPermissionPresenterTest {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -85,9 +91,9 @@ class RolesAndPermissionPresenterTest {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `present - DemoteSelfTo can handle failures and clean them`() = runTest(StandardTestDispatcher()) {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenUpdateUserRoleResult(Result.failure(Exception("Failed to update role")))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
updateUserRoleResult = { Result.failure(Exception("Failed to update role")) }
|
||||
)
|
||||
val presenter = createRolesAndPermissionsPresenter(room = room, dispatchers = testCoroutineDispatchers())
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -123,7 +129,12 @@ class RolesAndPermissionPresenterTest {
|
||||
@Test
|
||||
fun `present - ResetPermissions needs confirmation, then resets permissions`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val presenter = createRolesAndPermissionsPresenter(analyticsService = analyticsService)
|
||||
val presenter = createRolesAndPermissionsPresenter(
|
||||
analyticsService = analyticsService,
|
||||
room = FakeMatrixRoom(
|
||||
resetPowerLevelsResult = { Result.success(defaultRoomPowerLevels()) }
|
||||
)
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
|
||||
@@ -275,7 +275,10 @@ class ChangeRolesPresenterTest {
|
||||
|
||||
@Test
|
||||
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()))
|
||||
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 100)))
|
||||
}
|
||||
@@ -325,7 +328,10 @@ class ChangeRolesPresenterTest {
|
||||
@Test
|
||||
fun `present - Save will just save the data for moderators`() = runTest {
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val room = FakeMatrixRoom().apply {
|
||||
val room = FakeMatrixRoom(
|
||||
updateUserRoleResult = { Result.success(Unit) },
|
||||
updateMembersResult = { Result.success(Unit) },
|
||||
).apply {
|
||||
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))
|
||||
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
|
||||
}
|
||||
@@ -351,10 +357,11 @@ class ChangeRolesPresenterTest {
|
||||
|
||||
@Test
|
||||
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()))
|
||||
givenRoomInfo(aRoomInfo(userPowerLevels = persistentMapOf(A_USER_ID to 50)))
|
||||
givenUpdateUserRoleResult(Result.failure(IllegalStateException("Failed")))
|
||||
}
|
||||
val presenter = createChangeRolesPresenter(role = RoomMember.Role.MODERATOR, room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
||||
@@ -164,7 +164,13 @@ class ChangeRoomPermissionsPresenterTest {
|
||||
@Test
|
||||
fun `present - Save updates the current permissions and resets hasChanges`() = runTest {
|
||||
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) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
@@ -208,9 +214,9 @@ class ChangeRoomPermissionsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Save will fail if there are not current permissions`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenPowerLevelsResult(Result.failure(IllegalStateException("Failed to load power levels")))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
powerLevelsResult = { Result.failure(IllegalStateException("Failed to load power levels")) }
|
||||
)
|
||||
val presenter = createChangeRoomPermissionsPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -225,9 +231,10 @@ class ChangeRoomPermissionsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - Save can handle failures and they can be cleared`() = runTest {
|
||||
val room = FakeMatrixRoom().apply {
|
||||
givenUpdatePowerLevelsResult(Result.failure(IllegalStateException("Failed to update power levels")))
|
||||
}
|
||||
val room = FakeMatrixRoom(
|
||||
powerLevelsResult = { Result.success(defaultPermissions()) },
|
||||
updatePowerLevelsResult = { Result.failure(IllegalStateException("Failed to update power levels")) },
|
||||
)
|
||||
val presenter = createChangeRoomPermissionsPresenter(room = room)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
@@ -292,7 +299,9 @@ class ChangeRoomPermissionsPresenterTest {
|
||||
|
||||
private fun createChangeRoomPermissionsPresenter(
|
||||
section: ChangeRoomPermissionsSection = ChangeRoomPermissionsSection.RoomDetails,
|
||||
room: FakeMatrixRoom = FakeMatrixRoom(),
|
||||
room: FakeMatrixRoom = FakeMatrixRoom(
|
||||
powerLevelsResult = { Result.success(defaultPermissions()) }
|
||||
),
|
||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||
) = ChangeRoomPermissionsPresenter(
|
||||
section = section,
|
||||
|
||||
@@ -25,12 +25,13 @@
|
||||
<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_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_empty_state_title">"Δεν είσαι ακόμα σε κανένα δωμάτιο"</string>
|
||||
<string name="screen_roomlist_filter_unreads">"Μη αναγνωσμένα"</string>
|
||||
<string name="screen_roomlist_filter_unreads_empty_state_title">"Συγχαρητήρια!
|
||||
Δεν έχεις μη αναγνωσμένα μηνύματα!"</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_unread">"Επισήμανση ως μη αναγνωσμένου"</string>
|
||||
<string name="screen_roomlist_room_directory_button_title">"Περιήγηση σε όλα τα δωμάτια"</string>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?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">"თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე."</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_title">"მოწვევაზე უარის თქმა"</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
|
||||
fun `present - when set is favorite event is emitted, then the action is called`() = runTest {
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
val room = FakeMatrixRoom()
|
||||
val setIsFavoriteResult = lambdaRecorder { _: Boolean -> Result.success(Unit) }
|
||||
val room = FakeMatrixRoom(
|
||||
setIsFavoriteResult = setIsFavoriteResult
|
||||
)
|
||||
val analyticsService = FakeAnalyticsService()
|
||||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
@@ -452,9 +455,13 @@ class RoomListPresenterTest {
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
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))
|
||||
assertThat(room.setIsFavoriteCalls).isEqualTo(listOf(true, false))
|
||||
setIsFavoriteResult.assertions().isCalledExactly(2)
|
||||
.withSequence(
|
||||
listOf(value(true)),
|
||||
listOf(value(false)),
|
||||
)
|
||||
assertThat(analyticsService.capturedEvents).containsExactly(
|
||||
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_title">"გსურთ აღდგენის გასაღების შეცვლა?"</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_placeholder">"შეყვანა"</string>
|
||||
<string name="screen_recovery_key_confirm_success">"აღდგენის გასაღები დადასტურებულია"</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_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_ROOM_ID
|
||||
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.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
@@ -92,7 +93,9 @@ class SharePresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - send text ok`() = runTest {
|
||||
val matrixRoom = FakeMatrixRoom()
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
sendMessageResult = { _, _, _ -> Result.success(Unit) },
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, matrixRoom)
|
||||
}
|
||||
@@ -117,7 +120,9 @@ class SharePresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - send media ok`() = runTest {
|
||||
val matrixRoom = FakeMatrixRoom()
|
||||
val matrixRoom = FakeMatrixRoom(
|
||||
sendMediaResult = { Result.success(FakeMediaUploadHandler()) },
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, matrixRoom)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user