Merge branch 'develop' into feature/bma/android12

This commit is contained in:
Benoit Marty 2022-09-21 16:24:30 +02:00 committed by GitHub
commit b9c28ba4ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
155 changed files with 2783 additions and 620 deletions

View file

@ -31,7 +31,7 @@ jobs:
ui-tests: ui-tests:
name: UI Tests (Synapse) name: UI Tests (Synapse)
needs: should-i-run needs: should-i-run
runs-on: macos-latest runs-on: buildjet-4vcpu-ubuntu-2204
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:

View file

@ -13,7 +13,10 @@ env:
jobs: jobs:
tests: tests:
name: Runs all tests name: Runs all tests
runs-on: macos-latest # for the emulator runs-on: buildjet-4vcpu-ubuntu-2204
strategy:
matrix:
api-level: [28]
# Allow all jobs on main and develop. Just one per PR. # Allow all jobs on main and develop. Just one per PR.
concurrency: concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }} group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
@ -36,40 +39,70 @@ jobs:
httpPort: 8080 httpPort: 8080
disableRateLimiting: true disableRateLimiting: true
public_baseurl: "http://10.0.2.2:8080/" public_baseurl: "http://10.0.2.2:8080/"
- name: AVD cache
uses: actions/cache@v3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.api-level }}
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
arch: x86
profile: Nexus 5X
force-avd-creation: true # Is set to false in the doc https://github.com/ReactiveCircus/android-emulator-runner
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: echo "Generated AVD snapshot for caching."
- name: Run all the codecoverage tests at once - name: Run all the codecoverage tests at once
id: tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
continue-on-error: true # continue-on-error: true
with: with:
api-level: 28 api-level: ${{ matrix.api-level }}
arch: x86 arch: x86
profile: Nexus 5X profile: Nexus 5X
force-avd-creation: false force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true disable-animations: true
emulator-build: 7425822 # emulator-build: 7425822
script: | script: |
./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES ./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES ./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES ./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES ./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
# NB: continue-on-error marks steps.tests.conclusion = 'success' but leaves stes.tests.outcome = 'failure' # NB: continue-on-error marks steps.tests.conclusion = 'success' but leaves steps.tests.outcome = 'failure'
- name: Run all the codecoverage tests at once (retry if emulator failed) ### - name: Run all the codecoverage tests at once (retry if emulator failed)
uses: reactivecircus/android-emulator-runner@v2 ### uses: reactivecircus/android-emulator-runner@v2
if: always() && steps.tests.outcome == 'failure' # don't run if previous step succeeded. ### if: always() && steps.tests.outcome == 'failure' # don't run if previous step succeeded.
### with:
### api-level: 28
### arch: x86
### profile: Nexus 5X
### force-avd-creation: false
### emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
### disable-animations: true
### emulator-build: 7425822
### script: |
### ./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
### ./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
### ./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
### ./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
- name: Upload Integration Test Report Log
uses: actions/upload-artifact@v3
if: always()
with: with:
api-level: 28 name: integration-test-error-results
arch: x86 path: |
profile: Nexus 5X */build/outputs/androidTest-results/connected/
force-avd-creation: false */build/reports/androidTests/connected/
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
emulator-build: 7425822
script: |
./gradlew gatherGplayDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
./gradlew unitTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew instrumentationTestsWithCoverage $CI_GRADLE_ARG_PROPERTIES
./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
# we may have failed a previous step and retried, that's OK # we may have failed a previous step and retried, that's OK
- name: Publish results to Sonar - name: Publish results to Sonar

1
changelog.d/6970.wip Normal file
View file

@ -0,0 +1 @@
Create DM room only on first message - Add a spinner when sending the first message

1
changelog.d/7045.wip Normal file
View file

@ -0,0 +1 @@
[Device Manager] Filter Other Sessions

1
changelog.d/7079.bugfix Normal file
View file

@ -0,0 +1 @@
Fixed problem when room list's scroll did jump after rooms placeholders were replaced with rooms summary items

1
changelog.d/7108.misc Normal file
View file

@ -0,0 +1 @@
Move some GitHub actions to buildjet runners, and remove the second attempt to run integration tests.

1
changelog.d/7153.wip Normal file
View file

@ -0,0 +1 @@
Create DM room only on first message - Handle the local rooms within the new AppLayout

1
changelog.d/7166.misc Normal file
View file

@ -0,0 +1 @@
New App Layout is now enabled by default! Go to the Settings > Labs to toggle this

1
changelog.d/7180.feature Normal file
View file

@ -0,0 +1 @@
Deferred DMs - Enable and move the feature to labs settings

1
changelog.d/7186.bugfix Normal file
View file

@ -0,0 +1 @@
Fixes Room List not getting updated when fragment is not in focus

View file

@ -320,7 +320,7 @@
<string name="settings_theme">السمة</string> <string name="settings_theme">السمة</string>
<string name="encryption_information_decryption_error">خطأ في فكّ التعمية</string> <string name="encryption_information_decryption_error">خطأ في فكّ التعمية</string>
<string name="encryption_information_device_name">اسم الجهاز</string> <string name="encryption_information_device_name">اسم الجهاز</string>
<string name="device_manager_session_details_session_id">معرّف الجهاز</string> <string name="encryption_information_device_id">معرّف الجهاز</string>
<string name="encryption_information_device_key">مفتاح الجهاز</string> <string name="encryption_information_device_key">مفتاح الجهاز</string>
<string name="encryption_export_room_keys">صدّر مفاتيح الغرفة</string> <string name="encryption_export_room_keys">صدّر مفاتيح الغرفة</string>
<string name="encryption_export_room_keys_summary">صدّر المفاتيح إلى ملف محلي</string> <string name="encryption_export_room_keys_summary">صدّر المفاتيح إلى ملف محلي</string>

View file

@ -396,7 +396,7 @@
<string name="settings_theme">Тема</string> <string name="settings_theme">Тема</string>
<string name="encryption_information_decryption_error">Грешка при разшифроване</string> <string name="encryption_information_decryption_error">Грешка при разшифроване</string>
<string name="encryption_information_device_name">Публично име</string> <string name="encryption_information_device_name">Публично име</string>
<string name="device_manager_session_details_session_id">Сесийно ID</string> <string name="encryption_information_device_id">Сесийно ID</string>
<string name="encryption_information_device_key">Ключ на устройство</string> <string name="encryption_information_device_key">Ключ на устройство</string>
<string name="encryption_export_e2e_room_keys">Експортирай E2E ключове за стая</string> <string name="encryption_export_e2e_room_keys">Експортирай E2E ключове за стая</string>
<string name="encryption_export_room_keys">Експортиране на ключове за стая</string> <string name="encryption_export_room_keys">Експортиране на ключове за стая</string>

View file

@ -789,7 +789,7 @@
<string name="encryption_export_room_keys">রুমের কুঞ্জিগুলি এক্সপোর্ট করুন</string> <string name="encryption_export_room_keys">রুমের কুঞ্জিগুলি এক্সপোর্ট করুন</string>
<string name="encryption_export_e2e_room_keys">শেষ থেকে শেষ রুমের কুঞ্জিগুলি এক্সপোর্ট করুন</string> <string name="encryption_export_e2e_room_keys">শেষ থেকে শেষ রুমের কুঞ্জিগুলি এক্সপোর্ট করুন</string>
<string name="encryption_information_device_key">সেশানের কুঞ্জি</string> <string name="encryption_information_device_key">সেশানের কুঞ্জি</string>
<string name="device_manager_session_details_session_id">আইডি</string> <string name="encryption_information_device_id">আইডি</string>
<string name="encryption_information_device_name">সর্বজনীন নাম</string> <string name="encryption_information_device_name">সর্বজনীন নাম</string>
<string name="encryption_information_decryption_error">ডিক্রিপশন সমস্যা</string> <string name="encryption_information_decryption_error">ডিক্রিপশন সমস্যা</string>
<string name="settings_theme">থিম</string> <string name="settings_theme">থিম</string>

View file

@ -693,7 +693,7 @@
<string name="encryption_information_decryption_error">ডিক্রিপশন সমস্যা</string> <string name="encryption_information_decryption_error">ডিক্রিপশন সমস্যা</string>
<string name="encryption_information_device_name">সর্বজনীন নাম</string> <string name="encryption_information_device_name">সর্বজনীন নাম</string>
<string name="device_manager_session_details_session_id">আইডি</string> <string name="encryption_information_device_id">আইডি</string>
<string name="encryption_information_device_key">সেশানের কুঞ্জি</string> <string name="encryption_information_device_key">সেশানের কুঞ্জি</string>
<string name="encryption_export_e2e_room_keys">শেষ থেকে শেষ রুমের কুঞ্জিগুলি এক্সপোর্ট করুন</string> <string name="encryption_export_e2e_room_keys">শেষ থেকে শেষ রুমের কুঞ্জিগুলি এক্সপোর্ট করুন</string>

View file

@ -448,7 +448,7 @@
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="encryption_information_decryption_error">Error al desxifrar</string> <string name="encryption_information_decryption_error">Error al desxifrar</string>
<string name="encryption_information_device_name">Nom públic</string> <string name="encryption_information_device_name">Nom públic</string>
<string name="device_manager_session_details_session_id">ID de sessió</string> <string name="encryption_information_device_id">ID de sessió</string>
<string name="encryption_information_device_key">Clau de sessió</string> <string name="encryption_information_device_key">Clau de sessió</string>
<string name="encryption_export_e2e_room_keys">Exporta les claus de la sala E2E</string> <string name="encryption_export_e2e_room_keys">Exporta les claus de la sala E2E</string>
<string name="encryption_export_room_keys">Exporta les claus de la sala</string> <string name="encryption_export_room_keys">Exporta les claus de la sala</string>

View file

@ -635,7 +635,7 @@
<string name="settings_theme">Motiv vzhledu</string> <string name="settings_theme">Motiv vzhledu</string>
<string name="encryption_information_decryption_error">Chyba dešifrování</string> <string name="encryption_information_decryption_error">Chyba dešifrování</string>
<string name="encryption_information_device_name">Veřejné jméno</string> <string name="encryption_information_device_name">Veřejné jméno</string>
<string name="device_manager_session_details_session_id">ID relace</string> <string name="encryption_information_device_id">ID relace</string>
<string name="encryption_information_device_key">Klíč relace</string> <string name="encryption_information_device_key">Klíč relace</string>
<string name="encryption_export_e2e_room_keys">Export E2E klíčů místností</string> <string name="encryption_export_e2e_room_keys">Export E2E klíčů místností</string>
<string name="encryption_export_room_keys">Export klíčů místností</string> <string name="encryption_export_room_keys">Export klíčů místností</string>

View file

@ -418,7 +418,7 @@
<string name="room_settings_unset_main_address">Als Hauptadresse aufheben</string> <string name="room_settings_unset_main_address">Als Hauptadresse aufheben</string>
<string name="encryption_information_decryption_error">Entschlüsselungsfehler</string> <string name="encryption_information_decryption_error">Entschlüsselungsfehler</string>
<string name="encryption_information_device_name">Öffentlicher Name</string> <string name="encryption_information_device_name">Öffentlicher Name</string>
<string name="device_manager_session_details_session_id">Sitzungs-ID</string> <string name="encryption_information_device_id">Sitzungs-ID</string>
<string name="encryption_information_device_key">Sitzungsschlüssel</string> <string name="encryption_information_device_key">Sitzungsschlüssel</string>
<string name="encryption_export_e2e_room_keys">Ende-zu-Ende-Raumschlüssel exportieren</string> <string name="encryption_export_e2e_room_keys">Ende-zu-Ende-Raumschlüssel exportieren</string>
<string name="encryption_export_room_keys">Raumschlüssel exportieren</string> <string name="encryption_export_room_keys">Raumschlüssel exportieren</string>

View file

@ -172,7 +172,7 @@
<string name="settings_theme">Θέμα</string> <string name="settings_theme">Θέμα</string>
<string name="encryption_information_decryption_error">Σφάλμα αποκρυπτογράφησης</string> <string name="encryption_information_decryption_error">Σφάλμα αποκρυπτογράφησης</string>
<string name="encryption_information_device_name">Όνομα συσκευής</string> <string name="encryption_information_device_name">Όνομα συσκευής</string>
<string name="device_manager_session_details_session_id">Αναγνωριστικό συσκευής</string> <string name="encryption_information_device_id">Αναγνωριστικό συσκευής</string>
<string name="encryption_export_export">Εξαγωγή</string> <string name="encryption_export_export">Εξαγωγή</string>
<string name="encryption_import_import">Εισαγωγή</string> <string name="encryption_import_import">Εισαγωγή</string>
<string name="select_room_directory">Επιλέξτε ένα ευρετήριο δωματίων</string> <string name="select_room_directory">Επιλέξτε ένα ευρετήριο δωματίων</string>

View file

@ -1084,7 +1084,7 @@
<string name="encryption_export_room_keys">Elporti ŝlosilojn de ĉambroj</string> <string name="encryption_export_room_keys">Elporti ŝlosilojn de ĉambroj</string>
<string name="encryption_export_e2e_room_keys">Elporti tutvoje ĉifrajn ŝlosilojn de ĉambroj</string> <string name="encryption_export_e2e_room_keys">Elporti tutvoje ĉifrajn ŝlosilojn de ĉambroj</string>
<string name="encryption_information_device_key">Ŝlosilo de salutaĵo</string> <string name="encryption_information_device_key">Ŝlosilo de salutaĵo</string>
<string name="device_manager_session_details_session_id">Identigilo de salutaĵo</string> <string name="encryption_information_device_id">Identigilo de salutaĵo</string>
<string name="encryption_information_device_name">Publika nomo</string> <string name="encryption_information_device_name">Publika nomo</string>
<string name="encryption_information_decryption_error">Eraris malĉifrado</string> <string name="encryption_information_decryption_error">Eraris malĉifrado</string>
<string name="settings_theme">Haŭto</string> <string name="settings_theme">Haŭto</string>

View file

@ -249,7 +249,7 @@
<string name="room_settings_unset_main_address">Desescojer como Dirección Principal</string> <string name="room_settings_unset_main_address">Desescojer como Dirección Principal</string>
<string name="encryption_information_decryption_error">Error en descifrar</string> <string name="encryption_information_decryption_error">Error en descifrar</string>
<string name="encryption_information_device_name">Nombre del dispositivo</string> <string name="encryption_information_device_name">Nombre del dispositivo</string>
<string name="device_manager_session_details_session_id">Identificación del dispositivo</string> <string name="encryption_information_device_id">Identificación del dispositivo</string>
<string name="encryption_information_device_key">Clave del dispositivo</string> <string name="encryption_information_device_key">Clave del dispositivo</string>
<string name="encryption_export_e2e_room_keys">Exportar claves de cifrado de extremo-a-extremo de salas</string> <string name="encryption_export_e2e_room_keys">Exportar claves de cifrado de extremo-a-extremo de salas</string>
<string name="encryption_export_room_keys">Exportar claves de salas</string> <string name="encryption_export_room_keys">Exportar claves de salas</string>

View file

@ -415,7 +415,7 @@
<string name="room_settings_unset_main_address">Dejar de Establecer como dirección principal</string> <string name="room_settings_unset_main_address">Dejar de Establecer como dirección principal</string>
<string name="encryption_information_decryption_error">Error de descifrado</string> <string name="encryption_information_decryption_error">Error de descifrado</string>
<string name="encryption_information_device_name">Nombre público</string> <string name="encryption_information_device_name">Nombre público</string>
<string name="device_manager_session_details_session_id">ID de sesión</string> <string name="encryption_information_device_id">ID de sesión</string>
<string name="encryption_information_device_key">Clave de sesión</string> <string name="encryption_information_device_key">Clave de sesión</string>
<string name="encryption_export_e2e_room_keys">Exportar claves de salas con cifrado Extremo-a-Extremo</string> <string name="encryption_export_e2e_room_keys">Exportar claves de salas con cifrado Extremo-a-Extremo</string>
<string name="encryption_export_room_keys">Exportar claves de sala</string> <string name="encryption_export_room_keys">Exportar claves de sala</string>

View file

@ -612,7 +612,7 @@
<string name="room_settings_labs_warning_message">Need on alles katsejärgus olevad funktsionaalsused. Ole kasutamisel ettevaatlik.</string> <string name="room_settings_labs_warning_message">Need on alles katsejärgus olevad funktsionaalsused. Ole kasutamisel ettevaatlik.</string>
<string name="encryption_information_decryption_error">Dekrüptimise viga</string> <string name="encryption_information_decryption_error">Dekrüptimise viga</string>
<string name="encryption_information_device_name">Avalik nimi</string> <string name="encryption_information_device_name">Avalik nimi</string>
<string name="device_manager_session_details_session_id">Sessiooni tunnus</string> <string name="encryption_information_device_id">Sessiooni tunnus</string>
<string name="encryption_information_device_key">Sessiooni võti</string> <string name="encryption_information_device_key">Sessiooni võti</string>
<string name="encryption_export_e2e_room_keys">Ekspordi jututubade läbiva krüptimise võtmed</string> <string name="encryption_export_e2e_room_keys">Ekspordi jututubade läbiva krüptimise võtmed</string>
<string name="encryption_export_room_keys">Ekspordi jututoa võtmed</string> <string name="encryption_export_room_keys">Ekspordi jututoa võtmed</string>

View file

@ -406,7 +406,7 @@ Kontuan izan ekintza honek aplikazioa berrabiaraziko duela eta denbora bat behar
<string name="encryption_information_decryption_error">Deszifratze errorea</string> <string name="encryption_information_decryption_error">Deszifratze errorea</string>
<string name="encryption_information_device_name">Izen publikoa</string> <string name="encryption_information_device_name">Izen publikoa</string>
<string name="device_manager_session_details_session_id">IDa</string> <string name="encryption_information_device_id">IDa</string>
<string name="encryption_information_device_key">Saioaren gakoa</string> <string name="encryption_information_device_key">Saioaren gakoa</string>
<string name="encryption_export_e2e_room_keys">Esportatu E2E geletako gakoak</string> <string name="encryption_export_e2e_room_keys">Esportatu E2E geletako gakoak</string>

View file

@ -678,7 +678,7 @@
<string name="room_settings_labs_warning_message">این‌ها ویژگی‌های آزمایشی‌ای هستند که ممکن است به روش‌های نامنتظره‌ای حراب شوندا. با احتیاط استفاده کنید.</string> <string name="room_settings_labs_warning_message">این‌ها ویژگی‌های آزمایشی‌ای هستند که ممکن است به روش‌های نامنتظره‌ای حراب شوندا. با احتیاط استفاده کنید.</string>
<string name="room_settings_set_main_address">تنظیم به عنوان نشانی اصلی</string> <string name="room_settings_set_main_address">تنظیم به عنوان نشانی اصلی</string>
<string name="encryption_information_device_name">نام عمومی</string> <string name="encryption_information_device_name">نام عمومی</string>
<string name="device_manager_session_details_session_id">شناسهٔ نشست</string> <string name="encryption_information_device_id">شناسهٔ نشست</string>
<string name="encryption_information_device_key">کلید نشست</string> <string name="encryption_information_device_key">کلید نشست</string>
<string name="encryption_export_e2e_room_keys">برون‌ریزی کلید‌های اتاق‌های سرتاسری</string> <string name="encryption_export_e2e_room_keys">برون‌ریزی کلید‌های اتاق‌های سرتاسری</string>
<string name="encryption_export_room_keys">برون‌ریزی کلید‌های اتاق‌ها</string> <string name="encryption_export_room_keys">برون‌ریزی کلید‌های اتاق‌ها</string>

View file

@ -366,7 +366,7 @@
<string name="room_settings_unset_main_address">Kumoa pääosoitteeksi asettaminen</string> <string name="room_settings_unset_main_address">Kumoa pääosoitteeksi asettaminen</string>
<string name="encryption_information_decryption_error">Salauksenpurkuvirhe</string> <string name="encryption_information_decryption_error">Salauksenpurkuvirhe</string>
<string name="encryption_information_device_name">Julkinen nimi</string> <string name="encryption_information_device_name">Julkinen nimi</string>
<string name="device_manager_session_details_session_id">Istunnon tunnus</string> <string name="encryption_information_device_id">Istunnon tunnus</string>
<string name="encryption_information_device_key">Istunnon avain</string> <string name="encryption_information_device_key">Istunnon avain</string>
<string name="encryption_export_e2e_room_keys">Vie salatun huoneen avaimet</string> <string name="encryption_export_e2e_room_keys">Vie salatun huoneen avaimet</string>
<string name="encryption_export_room_keys">Vie huoneen avaimet</string> <string name="encryption_export_room_keys">Vie huoneen avaimet</string>

View file

@ -778,7 +778,7 @@
<string name="encryption_export_room_keys">Exporter les clés des salons</string> <string name="encryption_export_room_keys">Exporter les clés des salons</string>
<string name="encryption_export_e2e_room_keys">Exporter les clés E2E des salons</string> <string name="encryption_export_e2e_room_keys">Exporter les clés E2E des salons</string>
<string name="encryption_information_device_key">Clé de la session</string> <string name="encryption_information_device_key">Clé de la session</string>
<string name="device_manager_session_details_session_id">Identifiant de session</string> <string name="encryption_information_device_id">Identifiant de session</string>
<string name="encryption_information_device_name">Nom public</string> <string name="encryption_information_device_name">Nom public</string>
<string name="encryption_information_decryption_error">Erreur de déchiffrement</string> <string name="encryption_information_decryption_error">Erreur de déchiffrement</string>
<string name="settings_theme">Thème</string> <string name="settings_theme">Thème</string>

View file

@ -346,7 +346,7 @@
<string name="room_settings_unset_main_address">Désactiver comme adresse principale</string> <string name="room_settings_unset_main_address">Désactiver comme adresse principale</string>
<string name="encryption_information_decryption_error">Erreur de déchiffrement</string> <string name="encryption_information_decryption_error">Erreur de déchiffrement</string>
<string name="encryption_information_device_name">Nom public</string> <string name="encryption_information_device_name">Nom public</string>
<string name="device_manager_session_details_session_id">Identifiant de session</string> <string name="encryption_information_device_id">Identifiant de session</string>
<string name="encryption_information_device_key">Clé de la session</string> <string name="encryption_information_device_key">Clé de la session</string>
<string name="encryption_export_e2e_room_keys">Exporter les clés E2E des salons</string> <string name="encryption_export_e2e_room_keys">Exporter les clés E2E des salons</string>
<string name="encryption_export_room_keys">Exporter les clés des salons</string> <string name="encryption_export_room_keys">Exporter les clés des salons</string>

View file

@ -380,7 +380,7 @@
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="encryption_information_decryption_error">Fallo ao descifrar</string> <string name="encryption_information_decryption_error">Fallo ao descifrar</string>
<string name="encryption_information_device_name">Nome do dispositivo</string> <string name="encryption_information_device_name">Nome do dispositivo</string>
<string name="device_manager_session_details_session_id">ID de sesión</string> <string name="encryption_information_device_id">ID de sesión</string>
<string name="encryption_information_device_key">Chave do dispositivo</string> <string name="encryption_information_device_key">Chave do dispositivo</string>
<string name="encryption_export_e2e_room_keys">Exportar chaves E2E da sala</string> <string name="encryption_export_e2e_room_keys">Exportar chaves E2E da sala</string>
<string name="encryption_export_room_keys">Exportar chaves da sala</string> <string name="encryption_export_room_keys">Exportar chaves da sala</string>

View file

@ -572,7 +572,7 @@
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="encryption_information_decryption_error">Greška u dešifriranju</string> <string name="encryption_information_decryption_error">Greška u dešifriranju</string>
<string name="encryption_information_device_name">Javni naziv</string> <string name="encryption_information_device_name">Javni naziv</string>
<string name="device_manager_session_details_session_id">Identitet</string> <string name="encryption_information_device_id">Identitet</string>
<string name="encryption_information_device_key">Ključ sesije</string> <string name="encryption_information_device_key">Ključ sesije</string>
<string name="encryption_export_e2e_room_keys">Izvezi sobne ključeve za E2E</string> <string name="encryption_export_e2e_room_keys">Izvezi sobne ključeve za E2E</string>
<string name="encryption_export_room_keys">Izvezi sobne ključeve</string> <string name="encryption_export_room_keys">Izvezi sobne ključeve</string>

View file

@ -351,7 +351,7 @@
<string name="room_settings_unset_main_address">Kiszedés fő címek közül</string> <string name="room_settings_unset_main_address">Kiszedés fő címek közül</string>
<string name="encryption_information_decryption_error">Visszafejtés hiba</string> <string name="encryption_information_decryption_error">Visszafejtés hiba</string>
<string name="encryption_information_device_name">Nyilvános név</string> <string name="encryption_information_device_name">Nyilvános név</string>
<string name="device_manager_session_details_session_id">Munkamenet-azonosító</string> <string name="encryption_information_device_id">Munkamenet-azonosító</string>
<string name="encryption_information_device_key">Munkamenet kulcs</string> <string name="encryption_information_device_key">Munkamenet kulcs</string>
<string name="encryption_export_e2e_room_keys">E2E szoba kulcsok exportálása</string> <string name="encryption_export_e2e_room_keys">E2E szoba kulcsok exportálása</string>
<string name="encryption_export_room_keys">Szoba kulcsok exportálása</string> <string name="encryption_export_room_keys">Szoba kulcsok exportálása</string>

View file

@ -301,7 +301,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="encryption_information_decryption_error">Kesalahan dekripsi</string> <string name="encryption_information_decryption_error">Kesalahan dekripsi</string>
<string name="encryption_information_device_name">Nama perangkat</string> <string name="encryption_information_device_name">Nama perangkat</string>
<string name="device_manager_session_details_session_id">ID Sesi</string> <string name="encryption_information_device_id">ID Sesi</string>
<string name="encryption_information_device_key">Kunci perangkat</string> <string name="encryption_information_device_key">Kunci perangkat</string>
<string name="encryption_export_e2e_room_keys">Ekspor kunci ruangan terenkripsi</string> <string name="encryption_export_e2e_room_keys">Ekspor kunci ruangan terenkripsi</string>
<string name="encryption_export_room_keys">Ekspor ruangan kunci</string> <string name="encryption_export_room_keys">Ekspor ruangan kunci</string>

View file

@ -193,7 +193,7 @@
<string name="settings_theme">Þema</string> <string name="settings_theme">Þema</string>
<string name="encryption_information_decryption_error">Afkóðunarvilla</string> <string name="encryption_information_decryption_error">Afkóðunarvilla</string>
<string name="encryption_information_device_name">Heiti tækis</string> <string name="encryption_information_device_name">Heiti tækis</string>
<string name="device_manager_session_details_session_id">Auðkenni setu</string> <string name="encryption_information_device_id">Auðkenni setu</string>
<string name="encryption_information_device_key">Dulritunarlykill setu</string> <string name="encryption_information_device_key">Dulritunarlykill setu</string>
<string name="encryption_export_export">Flytja út</string> <string name="encryption_export_export">Flytja út</string>
<string name="passphrase_enter_passphrase">Settu inn lykilsetningu</string> <string name="passphrase_enter_passphrase">Settu inn lykilsetningu</string>

View file

@ -430,7 +430,7 @@
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="encryption_information_decryption_error">Errore di decriptazione</string> <string name="encryption_information_decryption_error">Errore di decriptazione</string>
<string name="encryption_information_device_name">Nome pubblico</string> <string name="encryption_information_device_name">Nome pubblico</string>
<string name="device_manager_session_details_session_id">ID sessione</string> <string name="encryption_information_device_id">ID sessione</string>
<string name="encryption_information_device_key">Chiave sessione</string> <string name="encryption_information_device_key">Chiave sessione</string>
<string name="encryption_export_e2e_room_keys">Esporta le chiavi di crittografia E2E delle stanze</string> <string name="encryption_export_e2e_room_keys">Esporta le chiavi di crittografia E2E delle stanze</string>
<string name="encryption_export_room_keys">Esporta le chiavi delle stanze</string> <string name="encryption_export_room_keys">Esporta le chiavi delle stanze</string>

View file

@ -542,7 +542,7 @@
<string name="encryption_export_room_keys">יצא מפתחות חדר</string> <string name="encryption_export_room_keys">יצא מפתחות חדר</string>
<string name="encryption_export_e2e_room_keys">ייצא מפתחות חדר E2E</string> <string name="encryption_export_e2e_room_keys">ייצא מפתחות חדר E2E</string>
<string name="encryption_information_device_key">מזהה מפתח</string> <string name="encryption_information_device_key">מזהה מפתח</string>
<string name="device_manager_session_details_session_id">מזהה מושב</string> <string name="encryption_information_device_id">מזהה מושב</string>
<string name="encryption_information_device_name">שם ציבורי</string> <string name="encryption_information_device_name">שם ציבורי</string>
<string name="encryption_information_decryption_error">שגיאת פענוח</string> <string name="encryption_information_decryption_error">שגיאת פענוח</string>
<string name="settings_theme">ערכת נושא</string> <string name="settings_theme">ערכת נושא</string>

View file

@ -197,7 +197,7 @@
<string name="room_settings_labs_warning_message">これらは予期しない不具合が生じるかもしれない実験的機能です。慎重に使用してください。</string> <string name="room_settings_labs_warning_message">これらは予期しない不具合が生じるかもしれない実験的機能です。慎重に使用してください。</string>
<string name="room_settings_set_main_address">メインアドレスとして設定</string> <string name="room_settings_set_main_address">メインアドレスとして設定</string>
<string name="room_settings_unset_main_address">メインアドレスとしての設定を解除</string> <string name="room_settings_unset_main_address">メインアドレスとしての設定を解除</string>
<string name="device_manager_session_details_session_id">セッションID</string> <string name="encryption_information_device_id">セッションID</string>
<string name="font_size">文字の大きさ</string> <string name="font_size">文字の大きさ</string>
<string name="tiny">とても小さい</string> <string name="tiny">とても小さい</string>
<string name="small">小さい</string> <string name="small">小さい</string>

View file

@ -291,7 +291,7 @@
<string name="room_settings_category_advanced_title">Talqayt</string> <string name="room_settings_category_advanced_title">Talqayt</string>
<string name="room_settings_labs_pref_title">Tinarimin</string> <string name="room_settings_labs_pref_title">Tinarimin</string>
<string name="settings_theme">Asentel</string> <string name="settings_theme">Asentel</string>
<string name="device_manager_session_details_session_id">Asulay n tqimit</string> <string name="encryption_information_device_id">Asulay n tqimit</string>
<string name="encryption_information_device_key">Tasarut n tɣimit</string> <string name="encryption_information_device_key">Tasarut n tɣimit</string>
<string name="encryption_export_e2e_room_keys">Sifeḍ tisura n texxamt E2E</string> <string name="encryption_export_e2e_room_keys">Sifeḍ tisura n texxamt E2E</string>
<string name="encryption_export_room_keys">Sifeḍ tisura n texxamt</string> <string name="encryption_export_room_keys">Sifeḍ tisura n texxamt</string>

View file

@ -431,7 +431,7 @@
<string name="settings_theme">테마</string> <string name="settings_theme">테마</string>
<string name="encryption_information_decryption_error">암호 복호화 오류</string> <string name="encryption_information_decryption_error">암호 복호화 오류</string>
<string name="encryption_information_device_name">공개 이름</string> <string name="encryption_information_device_name">공개 이름</string>
<string name="device_manager_session_details_session_id">ID</string> <string name="encryption_information_device_id">ID</string>
<string name="encryption_information_device_key">기기 키</string> <string name="encryption_information_device_key">기기 키</string>
<string name="encryption_export_e2e_room_keys">종단간 암호화 방 키 내보내기</string> <string name="encryption_export_e2e_room_keys">종단간 암호화 방 키 내보내기</string>
<string name="encryption_export_room_keys">방 키 내보내기</string> <string name="encryption_export_room_keys">방 키 내보내기</string>

View file

@ -909,7 +909,7 @@
<string name="encryption_export_room_keys">ສົ່ງອອກກະແຈຫ້ອງ</string> <string name="encryption_export_room_keys">ສົ່ງອອກກະແຈຫ້ອງ</string>
<string name="encryption_export_e2e_room_keys">ສົ່ງອອກກະແຈຫ້ອງ E2E</string> <string name="encryption_export_e2e_room_keys">ສົ່ງອອກກະແຈຫ້ອງ E2E</string>
<string name="encryption_information_device_key">ລະຫັດລະບົບ</string> <string name="encryption_information_device_key">ລະຫັດລະບົບ</string>
<string name="device_manager_session_details_session_id">ID ລະບົບ</string> <string name="encryption_information_device_id">ID ລະບົບ</string>
<string name="encryption_information_device_name">ຊື່ສາທາລະນະ</string> <string name="encryption_information_device_name">ຊື່ສາທາລະນະ</string>
<string name="encryption_information_decryption_error">ການຖອດລະຫັດຜິດພາດ</string> <string name="encryption_information_decryption_error">ການຖອດລະຫັດຜິດພາດ</string>
<string name="settings_theme">ຫົວຂໍ້</string> <string name="settings_theme">ຫົວຂໍ້</string>

View file

@ -469,7 +469,7 @@
<string name="settings_theme">Tēma</string> <string name="settings_theme">Tēma</string>
<string name="encryption_information_decryption_error">Atšifrēšanas kļūda</string> <string name="encryption_information_decryption_error">Atšifrēšanas kļūda</string>
<string name="encryption_information_device_name">Ierīces nosaukums</string> <string name="encryption_information_device_name">Ierīces nosaukums</string>
<string name="device_manager_session_details_session_id">Sesijas ID</string> <string name="encryption_information_device_id">Sesijas ID</string>
<string name="encryption_information_device_key">Sesijas atslēga</string> <string name="encryption_information_device_key">Sesijas atslēga</string>
<string name="encryption_export_e2e_room_keys">Eksportēt istabas šifrēšanas atslēgas</string> <string name="encryption_export_e2e_room_keys">Eksportēt istabas šifrēšanas atslēgas</string>
<string name="encryption_export_room_keys">Eksportēt istabas atslēgas</string> <string name="encryption_export_room_keys">Eksportēt istabas atslēgas</string>

View file

@ -119,7 +119,7 @@
<string name="room_settings_banned_users_title">Bannlyste brukere</string> <string name="room_settings_banned_users_title">Bannlyste brukere</string>
<string name="room_settings_category_advanced_title">Avansert</string> <string name="room_settings_category_advanced_title">Avansert</string>
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="device_manager_session_details_session_id">Økt-ID</string> <string name="encryption_information_device_id">Økt-ID</string>
<string name="encryption_information_device_key">Øktnøkkel</string> <string name="encryption_information_device_key">Øktnøkkel</string>
<string name="encryption_export_export">Eksporter</string> <string name="encryption_export_export">Eksporter</string>
<string name="encryption_import_import">Importer</string> <string name="encryption_import_import">Importer</string>

View file

@ -275,7 +275,7 @@
<string name="room_settings_unset_main_address">Niet instellen als hoofdadres</string> <string name="room_settings_unset_main_address">Niet instellen als hoofdadres</string>
<string name="encryption_information_decryption_error">Ontsleutelingsfout</string> <string name="encryption_information_decryption_error">Ontsleutelingsfout</string>
<string name="encryption_information_device_name">Publieke naam</string> <string name="encryption_information_device_name">Publieke naam</string>
<string name="device_manager_session_details_session_id">Sessie ID</string> <string name="encryption_information_device_id">Sessie ID</string>
<string name="encryption_information_device_key">Sessiesleutel</string> <string name="encryption_information_device_key">Sessiesleutel</string>
<string name="encryption_export_e2e_room_keys">E2E-gesprekssleutels exporteren</string> <string name="encryption_export_e2e_room_keys">E2E-gesprekssleutels exporteren</string>
<string name="encryption_export_room_keys">Gesprekssleutels exporteren</string> <string name="encryption_export_room_keys">Gesprekssleutels exporteren</string>

View file

@ -310,7 +310,7 @@
<string name="settings_theme">Preg</string> <string name="settings_theme">Preg</string>
<string name="encryption_information_decryption_error">Noko gjekk gale med dekrypteringa</string> <string name="encryption_information_decryption_error">Noko gjekk gale med dekrypteringa</string>
<string name="encryption_information_device_name">Offentleg namn</string> <string name="encryption_information_device_name">Offentleg namn</string>
<string name="device_manager_session_details_session_id">Økt-ID</string> <string name="encryption_information_device_id">Økt-ID</string>
<string name="encryption_information_device_key">Sesjonsnøkkel</string> <string name="encryption_information_device_key">Sesjonsnøkkel</string>
<string name="encryption_export_e2e_room_keys">Eksporter E2E-romnøkklar</string> <string name="encryption_export_e2e_room_keys">Eksporter E2E-romnøkklar</string>
<string name="encryption_export_room_keys">Eksporter romnøkklar</string> <string name="encryption_export_room_keys">Eksporter romnøkklar</string>

View file

@ -231,7 +231,7 @@
<string name="room_settings_set_main_address">Ustaw jako główny adres</string> <string name="room_settings_set_main_address">Ustaw jako główny adres</string>
<string name="settings_theme">Motyw</string> <string name="settings_theme">Motyw</string>
<string name="encryption_information_device_name">Nazwa publiczna</string> <string name="encryption_information_device_name">Nazwa publiczna</string>
<string name="device_manager_session_details_session_id">ID sesji</string> <string name="encryption_information_device_id">ID sesji</string>
<string name="encryption_export_export">Eksportuj</string> <string name="encryption_export_export">Eksportuj</string>
<string name="passphrase_enter_passphrase">Wprowadź hasło</string> <string name="passphrase_enter_passphrase">Wprowadź hasło</string>
<string name="passphrase_confirm_passphrase">Potwierdź hasło</string> <string name="passphrase_confirm_passphrase">Potwierdź hasło</string>

View file

@ -418,7 +418,7 @@
<string name="room_settings_unset_main_address">Des-definir como endereço principal</string> <string name="room_settings_unset_main_address">Des-definir como endereço principal</string>
<string name="encryption_information_decryption_error">Erro de decriptação</string> <string name="encryption_information_decryption_error">Erro de decriptação</string>
<string name="encryption_information_device_name">Nome público</string> <string name="encryption_information_device_name">Nome público</string>
<string name="device_manager_session_details_session_id">ID de sessão</string> <string name="encryption_information_device_id">ID de sessão</string>
<string name="encryption_information_device_key">Chave de sessão</string> <string name="encryption_information_device_key">Chave de sessão</string>
<string name="encryption_export_e2e_room_keys">Exportar chaves de sala E2E</string> <string name="encryption_export_e2e_room_keys">Exportar chaves de sala E2E</string>
<string name="encryption_export_room_keys">Exportar chaves de sala</string> <string name="encryption_export_room_keys">Exportar chaves de sala</string>

View file

@ -246,7 +246,7 @@ Note que esta acção irá reiniciar a aplicação e poderá levar algum tempo.<
<string name="encryption_information_decryption_error">Erro de decifragem</string> <string name="encryption_information_decryption_error">Erro de decifragem</string>
<string name="encryption_information_device_name">Nome do dispositivo</string> <string name="encryption_information_device_name">Nome do dispositivo</string>
<string name="device_manager_session_details_session_id">ID do dispositivo</string> <string name="encryption_information_device_id">ID do dispositivo</string>
<string name="encryption_information_device_key">Chave do dispositivo</string> <string name="encryption_information_device_key">Chave do dispositivo</string>
<string name="encryption_export_e2e_room_keys">Exportar chaves E2E da sala</string> <string name="encryption_export_e2e_room_keys">Exportar chaves E2E da sala</string>
<string name="encryption_export_room_keys">Exportar chaves de sala</string> <string name="encryption_export_room_keys">Exportar chaves de sala</string>

View file

@ -432,7 +432,7 @@
<string name="room_settings_unset_main_address">Сбросить основной адрес</string> <string name="room_settings_unset_main_address">Сбросить основной адрес</string>
<string name="encryption_information_decryption_error">Ошибка дешифровки</string> <string name="encryption_information_decryption_error">Ошибка дешифровки</string>
<string name="encryption_information_device_name">Публичное имя</string> <string name="encryption_information_device_name">Публичное имя</string>
<string name="device_manager_session_details_session_id">ID сессии</string> <string name="encryption_information_device_id">ID сессии</string>
<string name="encryption_information_device_key">Ключ сессии</string> <string name="encryption_information_device_key">Ключ сессии</string>
<string name="encryption_export_e2e_room_keys">Экспорт E2E ключей комнаты</string> <string name="encryption_export_e2e_room_keys">Экспорт E2E ключей комнаты</string>
<string name="encryption_export_room_keys">Экспорт ключей комнаты</string> <string name="encryption_export_room_keys">Экспорт ключей комнаты</string>

View file

@ -388,7 +388,7 @@
<string name="settings_theme">Vzhľad</string> <string name="settings_theme">Vzhľad</string>
<string name="encryption_information_decryption_error">Chyba dešifrovania</string> <string name="encryption_information_decryption_error">Chyba dešifrovania</string>
<string name="encryption_information_device_name">Verejné meno</string> <string name="encryption_information_device_name">Verejné meno</string>
<string name="device_manager_session_details_session_id">ID relácie</string> <string name="encryption_information_device_id">ID relácie</string>
<string name="encryption_information_device_key">Kľúč relácie</string> <string name="encryption_information_device_key">Kľúč relácie</string>
<string name="encryption_export_e2e_room_keys">Exportovať šifrovacie kľúče miestnosti</string> <string name="encryption_export_e2e_room_keys">Exportovať šifrovacie kľúče miestnosti</string>
<string name="encryption_export_room_keys">Exportovať kľúče miestnosti</string> <string name="encryption_export_room_keys">Exportovať kľúče miestnosti</string>

View file

@ -431,7 +431,7 @@
<string name="settings_theme">Temë</string> <string name="settings_theme">Temë</string>
<string name="encryption_information_decryption_error">Gabim shfshehtëzimi</string> <string name="encryption_information_decryption_error">Gabim shfshehtëzimi</string>
<string name="encryption_information_device_name">Emër publik</string> <string name="encryption_information_device_name">Emër publik</string>
<string name="device_manager_session_details_session_id">ID Sesioni</string> <string name="encryption_information_device_id">ID Sesioni</string>
<string name="encryption_information_device_key">Kyç sesioni</string> <string name="encryption_information_device_key">Kyç sesioni</string>
<string name="encryption_export_e2e_room_keys">Eksporto kyçe dhome E2E</string> <string name="encryption_export_e2e_room_keys">Eksporto kyçe dhome E2E</string>
<string name="encryption_export_room_keys">Eksporto kyçe dhome</string> <string name="encryption_export_room_keys">Eksporto kyçe dhome</string>

View file

@ -918,7 +918,7 @@
<string name="settings_secure_backup_enter_to_setup">Sätt upp på den här enheten</string> <string name="settings_secure_backup_enter_to_setup">Sätt upp på den här enheten</string>
<string name="reset_secure_backup_title">Generera en ny säkerhetskopia eller sätt en ny lösenfras för din existerande säkerhetskopia.</string> <string name="reset_secure_backup_title">Generera en ny säkerhetskopia eller sätt en ny lösenfras för din existerande säkerhetskopia.</string>
<string name="room_settings_labs_warning_message">Detta är experimentella funktioner som kan gå sönder på oväntade sätt. Använd varsamt.</string> <string name="room_settings_labs_warning_message">Detta är experimentella funktioner som kan gå sönder på oväntade sätt. Använd varsamt.</string>
<string name="device_manager_session_details_session_id">Sessions-ID</string> <string name="encryption_information_device_id">Sessions-ID</string>
<string name="encryption_information_device_key">Sessionsnyckel</string> <string name="encryption_information_device_key">Sessionsnyckel</string>
<string name="encryption_export_e2e_room_keys">Exportera krypteringsnycklar</string> <string name="encryption_export_e2e_room_keys">Exportera krypteringsnycklar</string>
<string name="encryption_export_room_keys">Exportera rumsnycklar</string> <string name="encryption_export_room_keys">Exportera rumsnycklar</string>

View file

@ -260,7 +260,7 @@
<string name="room_settings_set_main_address">ప్రధాన చిరునామాగా సెట్ చేయండి</string> <string name="room_settings_set_main_address">ప్రధాన చిరునామాగా సెట్ చేయండి</string>
<string name="encryption_information_device_name">పరికరం పేరు</string> <string name="encryption_information_device_name">పరికరం పేరు</string>
<string name="device_manager_session_details_session_id">పరికరం ID</string> <string name="encryption_information_device_id">పరికరం ID</string>
<string name="encryption_information_device_key">పరికరం కీ</string> <string name="encryption_information_device_key">పరికరం కీ</string>
<string name="encryption_export_e2e_room_keys">E2E గది కీలను ఎగుమతి చేయండి</string> <string name="encryption_export_e2e_room_keys">E2E గది కీలను ఎగుమతి చేయండి</string>

View file

@ -376,7 +376,7 @@
<string name="settings_theme">Tema</string> <string name="settings_theme">Tema</string>
<string name="encryption_information_decryption_error">Çözme hatası</string> <string name="encryption_information_decryption_error">Çözme hatası</string>
<string name="encryption_information_device_name">Görünür Ad</string> <string name="encryption_information_device_name">Görünür Ad</string>
<string name="device_manager_session_details_session_id">Oturum kimliği</string> <string name="encryption_information_device_id">Oturum kimliği</string>
<string name="encryption_information_device_key">Oturum anahtarı</string> <string name="encryption_information_device_key">Oturum anahtarı</string>
<string name="encryption_export_e2e_room_keys">E2E Oda anahtarlarını dışa aktar</string> <string name="encryption_export_e2e_room_keys">E2E Oda anahtarlarını dışa aktar</string>
<string name="encryption_export_room_keys">Oda anahtarlarını dışa aktar</string> <string name="encryption_export_room_keys">Oda anahtarlarını dışa aktar</string>

View file

@ -354,7 +354,7 @@
<string name="room_settings_unset_main_address">Зробити не основною адресою</string> <string name="room_settings_unset_main_address">Зробити не основною адресою</string>
<string name="encryption_information_decryption_error">Помилка розшифрування</string> <string name="encryption_information_decryption_error">Помилка розшифрування</string>
<string name="encryption_information_device_name">Загальнодоступна назва</string> <string name="encryption_information_device_name">Загальнодоступна назва</string>
<string name="device_manager_session_details_session_id">ID сеансу</string> <string name="encryption_information_device_id">ID сеансу</string>
<string name="encryption_information_device_key">Ключ сеансу</string> <string name="encryption_information_device_key">Ключ сеансу</string>
<string name="encryption_export_e2e_room_keys">Експортувати E2E ключі кімнати</string> <string name="encryption_export_e2e_room_keys">Експортувати E2E ключі кімнати</string>
<string name="encryption_export_room_keys">Експортувати ключі кімнати</string> <string name="encryption_export_room_keys">Експортувати ключі кімнати</string>

View file

@ -594,7 +594,7 @@
<string name="deactivate_account_title">Hủy tài khoản</string> <string name="deactivate_account_title">Hủy tài khoản</string>
<string name="dialog_user_consent_submit">Xem lại ngay</string> <string name="dialog_user_consent_submit">Xem lại ngay</string>
<string name="encryption_information_device_key">Chìa khóa phiên</string> <string name="encryption_information_device_key">Chìa khóa phiên</string>
<string name="device_manager_session_details_session_id">Mã phiên</string> <string name="encryption_information_device_id">Mã phiên</string>
<string name="encryption_information_device_name">Tên công khai</string> <string name="encryption_information_device_name">Tên công khai</string>
<string name="encryption_information_decryption_error">Lỗi giải mã</string> <string name="encryption_information_decryption_error">Lỗi giải mã</string>
<string name="room_settings_labs_warning_message">Những chức năng này mang tính thí nghiệm có thể còn nhiều lỗi. Lưu ý khi dùng.</string> <string name="room_settings_labs_warning_message">Những chức năng này mang tính thí nghiệm có thể còn nhiều lỗi. Lưu ý khi dùng.</string>

View file

@ -242,7 +242,7 @@
<string name="settings_password_updated">你的密码已更新</string> <string name="settings_password_updated">你的密码已更新</string>
<string name="encryption_information_decryption_error">解密错误</string> <string name="encryption_information_decryption_error">解密错误</string>
<string name="encryption_information_device_name">公开名称</string> <string name="encryption_information_device_name">公开名称</string>
<string name="device_manager_session_details_session_id">会话 ID</string> <string name="encryption_information_device_id">会话 ID</string>
<string name="encryption_information_device_key">会话密钥</string> <string name="encryption_information_device_key">会话密钥</string>
<string name="encryption_import_import">导入</string> <string name="encryption_import_import">导入</string>
<string name="encryption_information_verified">已验证</string> <string name="encryption_information_verified">已验证</string>

View file

@ -469,7 +469,7 @@
<string name="settings_theme">主題</string> <string name="settings_theme">主題</string>
<string name="encryption_information_decryption_error">解密錯誤</string> <string name="encryption_information_decryption_error">解密錯誤</string>
<string name="encryption_information_device_name">公開名稱</string> <string name="encryption_information_device_name">公開名稱</string>
<string name="device_manager_session_details_session_id">工作階段 ID</string> <string name="encryption_information_device_id">工作階段 ID</string>
<string name="encryption_information_device_key">工作階段金鑰</string> <string name="encryption_information_device_key">工作階段金鑰</string>
<string name="encryption_export_e2e_room_keys">匯出聊天室的端到端加密金鑰</string> <string name="encryption_export_e2e_room_keys">匯出聊天室的端到端加密金鑰</string>
<string name="encryption_export_room_keys">匯出聊天室的加密金鑰</string> <string name="encryption_export_room_keys">匯出聊天室的加密金鑰</string>

View file

@ -442,6 +442,9 @@
<string name="labs_enable_new_app_layout_title">Enable new layout</string> <string name="labs_enable_new_app_layout_title">Enable new layout</string>
<string name="labs_enable_new_app_layout_summary">A simplified Element with optional tabs</string> <string name="labs_enable_new_app_layout_summary">A simplified Element with optional tabs</string>
<string name="labs_enable_deferred_dm_title">Enable deferred DMs</string>
<string name="labs_enable_deferred_dm_summary">Create DM only on first message</string>
<!-- Home fragment --> <!-- Home fragment -->
<string name="invitations_header">Invites</string> <string name="invitations_header">Invites</string>
<string name="low_priority_header">Low priority</string> <string name="low_priority_header">Low priority</string>
@ -2361,7 +2364,9 @@
<string name="settings_active_sessions_manage">Manage Sessions</string> <string name="settings_active_sessions_manage">Manage Sessions</string>
<string name="settings_active_sessions_signout_device">Sign out of this session</string> <string name="settings_active_sessions_signout_device">Sign out of this session</string>
<string name="settings_sessions_list">Sessions</string> <string name="settings_sessions_list">Sessions</string>
<!-- TODO rename to device_manager_sessions_other_title -->
<string name="settings_sessions_other_title">Other sessions</string> <string name="settings_sessions_other_title">Other sessions</string>
<!-- TODO rename to device_manager_sessions_other_description -->
<string name="settings_sessions_other_description">For best security, verify your sessions and sign out from any session that you dont recognize or use anymore.</string> <string name="settings_sessions_other_description">For best security, verify your sessions and sign out from any session that you dont recognize or use anymore.</string>
<string name="settings_server_name">Server name</string> <string name="settings_server_name">Server name</string>
@ -3265,10 +3270,36 @@
<string name="device_manager_device_title">Device</string> <string name="device_manager_device_title">Device</string>
<!-- Examples: Last activity Yesterday at 6PM, Last activity Aug 31 at 5:47PM --> <!-- Examples: Last activity Yesterday at 6PM, Last activity Aug 31 at 5:47PM -->
<string name="device_manager_session_last_activity">Last activity %1$s</string> <string name="device_manager_session_last_activity">Last activity %1$s</string>
<string name="device_manager_filter_bottom_sheet_title">Filter</string>
<string name="device_manager_filter_option_all_sessions">All sessions</string>
<string name="device_manager_filter_option_verified">Verified</string>
<string name="device_manager_filter_option_verified_description">Ready for secure messaging</string>
<string name="device_manager_filter_option_unverified">Unverified</string>
<string name="device_manager_filter_option_unverified_description">Not ready for secure messaging</string>
<string name="device_manager_filter_option_inactive">Inactive</string>
<plurals name="device_manager_filter_option_inactive_description">
<item quantity="one">Inactive for %1$d day or longer</item>
<item quantity="other">Inactive for %1$d days or longer</item>
</plurals>
<string name="a11y_device_manager_filter">Filter</string>
<string name="device_manager_other_sessions_recommendation_title_verified">Verified</string>
<string name="device_manager_other_sessions_recommendation_description_verified">For best security, sign out from any session that you dont recognize or use anymore.</string>
<string name="device_manager_other_sessions_recommendation_title_unverified">Unverified</string>
<string name="device_manager_other_sessions_recommendation_description_unverified">Verify your sessions for enhanced secure messaging or sign out from those you dont recognize or use anymore.</string>
<string name="device_manager_other_sessions_recommendation_title_inactive">Inactive</string>
<plurals name="device_manager_other_sessions_recommendation_description_inactive">
<item quantity="one">Consider signing out from old sessions (%1$d day or more) you dont use anymore.</item>
<item quantity="other">Consider signing out from old sessions (%1$d days or more) you dont use anymore.</item>
</plurals>
<string name="device_manager_other_sessions_no_verified_sessions_found">No verified sessions found.</string>
<string name="device_manager_other_sessions_no_unverified_sessions_found">No unverified sessions found.</string>
<string name="device_manager_other_sessions_no_inactive_sessions_found">No inactive sessions found.</string>
<string name="device_manager_other_sessions_clear_filter">Clear Filter</string>
<string name="device_manager_session_details_title">Session details</string> <string name="device_manager_session_details_title">Session details</string>
<string name="device_manager_session_details_description">Application, device, and activity information.</string> <string name="device_manager_session_details_description">Application, device, and activity information.</string>
<string name="device_manager_session_details_session_name">Session name</string> <string name="device_manager_session_details_session_name">Session name</string>
<string name="device_manager_session_details_session_id">Session ID</string> <!-- TODO rename to device_manager_session_details_session_id -->
<string name="encryption_information_device_id">Session ID</string>
<string name="device_manager_session_details_session_last_activity">Last activity</string> <string name="device_manager_session_details_session_last_activity">Last activity</string>
<string name="device_manager_session_details_device_ip_address">IP address</string> <string name="device_manager_session_details_device_ip_address">IP address</string>

View file

@ -141,6 +141,7 @@
<!-- Shield colors --> <!-- Shield colors -->
<color name="shield_color_trust">#0DBD8B</color> <color name="shield_color_trust">#0DBD8B</color>
<color name="shield_color_trust_background">#0F0DBD8B</color>
<color name="shield_color_black">#17191C</color> <color name="shield_color_black">#17191C</color>
<color name="shield_color_warning">#FF4B55</color> <color name="shield_color_warning">#FF4B55</color>
<color name="shield_color_warning_background">#0FFF4B55</color> <color name="shield_color_warning_background">#0FFF4B55</color>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="OtherSessionsSecurityRecommendationView">
<attr name="otherSessionsRecommendationTitle" format="string" />
<attr name="otherSessionsRecommendationDescription" format="string" />
<attr name="otherSessionsRecommendationImageResource" format="reference" />
<attr name="otherSessionsRecommendationImageBackgroundTint" format="color" />
</declare-styleable>
</resources>

View file

@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.getTimelineEvent import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -46,6 +47,13 @@ class FlowRoom(private val room: Room) {
} }
} }
fun liveLocalRoomSummary(): Flow<Optional<LocalRoomSummary>> {
return room.getLocalRoomSummaryLive().asFlow()
.startWith(room.coroutineDispatchers.io) {
room.localRoomSummary().toOptional()
}
}
fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow<List<RoomMemberSummary>> { fun liveRoomMembers(queryParams: RoomMemberQueryParams): Flow<List<RoomMemberSummary>> {
return room.membershipService().getRoomMembersLive(queryParams).asFlow() return room.membershipService().getRoomMembersLive(queryParams).asFlow()
.startWith(room.coroutineDispatchers.io) { .startWith(room.coroutineDispatchers.io) {

View file

@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
import org.matrix.android.sdk.api.session.room.location.LocationSharingService import org.matrix.android.sdk.api.session.room.location.LocationSharingService
import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.MembershipService
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.relation.RelationService import org.matrix.android.sdk.api.session.room.model.relation.RelationService
import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService
@ -60,11 +61,22 @@ interface Room {
*/ */
fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>>
/**
* A live [LocalRoomSummary] associated with the room.
* You can observe this summary to get dynamic data from this room.
*/
fun getLocalRoomSummaryLive(): LiveData<Optional<LocalRoomSummary>>
/** /**
* A current snapshot of [RoomSummary] associated with the room. * A current snapshot of [RoomSummary] associated with the room.
*/ */
fun roomSummary(): RoomSummary? fun roomSummary(): RoomSummary?
/**
* A current snapshot of [LocalRoomSummary] associated with the room.
*/
fun localRoomSummary(): LocalRoomSummary?
/** /**
* Use this room as a Space, if the type is correct. * Use this room as a Space, if the type is correct.
*/ */

View file

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.identity.model.SignInvitationResult import org.matrix.android.sdk.api.session.identity.model.SignInvitationResult
import org.matrix.android.sdk.api.session.room.alias.RoomAliasDescription import org.matrix.android.sdk.api.session.room.alias.RoomAliasDescription
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -117,6 +118,12 @@ interface RoomService {
*/ */
fun getRoomSummaryLive(roomId: String): LiveData<Optional<RoomSummary>> fun getRoomSummaryLive(roomId: String): LiveData<Optional<RoomSummary>>
/**
* A live [LocalRoomSummary] associated with the room with id [roomId].
* You can observe this summary to get dynamic data from this room, even if the room is not joined yet
*/
fun getLocalRoomSummaryLive(roomId: String): LiveData<Optional<LocalRoomSummary>>
/** /**
* Get a snapshot list of room summaries. * Get a snapshot list of room summaries.
* @return the immutable list of [RoomSummary] * @return the immutable list of [RoomSummary]

View file

@ -0,0 +1,24 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.model
enum class LocalRoomCreationState {
NOT_CREATED,
CREATING,
FAILURE,
CREATED
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.model
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
/**
* This class holds some data of a local room.
* It can be retrieved by [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]
*/
data class LocalRoomSummary(
/**
* The roomId of the room.
*/
val roomId: String,
/**
* The room summary of the room.
*/
val roomSummary: RoomSummary?,
/**
* The creation params attached to the room.
*/
val createRoomParams: CreateRoomParams?,
/**
* The roomId of the created room (ie. created on the server), if any.
*/
val replacementRoomId: String?,
/**
* The creation state of the room.
*/
val creationState: LocalRoomCreationState,
)

View file

@ -53,6 +53,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo033
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject import javax.inject.Inject
@ -61,7 +62,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer private val normalizer: Normalizer
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Session", dbName = "Session",
schemaVersion = 36L, schemaVersion = 37L,
) { ) {
/** /**
* Forces all RealmSessionStoreMigration instances to be equal. * Forces all RealmSessionStoreMigration instances to be equal.
@ -107,5 +108,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 34) MigrateSessionTo034(realm).perform() if (oldVersion < 34) MigrateSessionTo034(realm).perform()
if (oldVersion < 35) MigrateSessionTo035(realm).perform() if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform() if (oldVersion < 36) MigrateSessionTo036(realm).perform()
if (oldVersion < 37) MigrateSessionTo037(realm).perform()
} }
} }

View file

@ -0,0 +1,36 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.mapper
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import javax.inject.Inject
internal class LocalRoomSummaryMapper @Inject constructor(
private val roomSummaryMapper: RoomSummaryMapper,
) {
fun map(localRoomSummaryEntity: LocalRoomSummaryEntity): LocalRoomSummary {
return LocalRoomSummary(
roomId = localRoomSummaryEntity.roomId,
roomSummary = localRoomSummaryEntity.roomSummaryEntity?.let { roomSummaryMapper.map(it) },
createRoomParams = localRoomSummaryEntity.createRoomParams,
replacementRoomId = localRoomSummaryEntity.replacementRoomId,
creationState = localRoomSummaryEntity.creationState
)
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
internal class MigrateSessionTo037(realm: DynamicRealm) : RealmMigrator(realm, 37) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("LocalRoomSummaryEntity")
?.addField(LocalRoomSummaryEntityFields.REPLACEMENT_ROOM_ID, String::class.java)
?.addField(LocalRoomSummaryEntityFields.STATE_STR, String::class.java)
?.transform { obj ->
obj.set(LocalRoomSummaryEntityFields.STATE_STR, LocalRoomCreationState.NOT_CREATED.name)
}
}
}

View file

@ -18,15 +18,24 @@ package org.matrix.android.sdk.internal.database.model
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.toJSONString import org.matrix.android.sdk.api.session.room.model.create.toJSONString
internal open class LocalRoomSummaryEntity( internal open class LocalRoomSummaryEntity(
@PrimaryKey var roomId: String = "", @PrimaryKey var roomId: String = "",
var roomSummaryEntity: RoomSummaryEntity? = null, var roomSummaryEntity: RoomSummaryEntity? = null,
private var createRoomParamsStr: String? = null var replacementRoomId: String? = null,
) : RealmObject() { ) : RealmObject() {
private var stateStr: String = LocalRoomCreationState.NOT_CREATED.name
var creationState: LocalRoomCreationState
get() = LocalRoomCreationState.valueOf(stateStr)
set(value) {
stateStr = value.name
}
private var createRoomParamsStr: String? = null
var createRoomParams: CreateRoomParams? var createRoomParams: CreateRoomParams?
get() { get() {
return CreateRoomParams.fromJson(createRoomParamsStr) return CreateRoomParams.fromJson(createRoomParamsStr)

View file

@ -22,10 +22,6 @@ import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
internal fun LocalRoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<LocalRoomSummaryEntity> { internal fun LocalRoomSummaryEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<LocalRoomSummaryEntity> {
val query = realm.where<LocalRoomSummaryEntity>() return realm.where<LocalRoomSummaryEntity>().equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId)
if (roomId != null) {
query.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, roomId)
}
return query
} }

View file

@ -33,6 +33,11 @@ internal fun ReadReceiptEntity.Companion.whereUserId(realm: Realm, userId: Strin
.equalTo(ReadReceiptEntityFields.USER_ID, userId) .equalTo(ReadReceiptEntityFields.USER_ID, userId)
} }
internal fun ReadReceiptEntity.Companion.whereRoomId(realm: Realm, roomId: String): RealmQuery<ReadReceiptEntity> {
return realm.where<ReadReceiptEntity>()
.equalTo(ReadReceiptEntityFields.ROOM_ID, roomId)
}
internal fun ReadReceiptEntity.Companion.createUnmanaged(roomId: String, eventId: String, userId: String, originServerTs: Double): ReadReceiptEntity { internal fun ReadReceiptEntity.Companion.createUnmanaged(roomId: String, eventId: String, userId: String, originServerTs: Double): ReadReceiptEntity {
return ReadReceiptEntity().apply { return ReadReceiptEntity().apply {
this.primaryKey = "${roomId}_$userId" this.primaryKey = "${roomId}_$userId"

View file

@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.room.call.RoomCallService
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
import org.matrix.android.sdk.api.session.room.location.LocationSharingService import org.matrix.android.sdk.api.session.room.location.LocationSharingService
import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.MembershipService
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.relation.RelationService import org.matrix.android.sdk.api.session.room.model.relation.RelationService
@ -82,6 +83,14 @@ internal class DefaultRoom(
return roomSummaryDataSource.getRoomSummary(roomId) return roomSummaryDataSource.getRoomSummary(roomId)
} }
override fun getLocalRoomSummaryLive(): LiveData<Optional<LocalRoomSummary>> {
return roomSummaryDataSource.getLocalRoomSummaryLive(roomId)
}
override fun localRoomSummary(): LocalRoomSummary? {
return roomSummaryDataSource.getLocalRoomSummary(roomId)
}
override fun asSpace(): Space? { override fun asSpace(): Space? {
if (roomSummary()?.roomType != RoomType.SPACE) return null if (roomSummary()?.roomType != RoomType.SPACE) return null
return DefaultSpace(this, roomSummaryDataSource, viaParameterFinder) return DefaultSpace(this, roomSummaryDataSource, viaParameterFinder)

View file

@ -29,10 +29,12 @@ import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
import org.matrix.android.sdk.api.session.room.alias.RoomAliasDescription import org.matrix.android.sdk.api.session.room.alias.RoomAliasDescription
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.peeking.PeekResult
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
@ -106,6 +108,10 @@ internal class DefaultRoomService @Inject constructor(
return roomSummaryDataSource.getRoomSummaryLive(roomId) return roomSummaryDataSource.getRoomSummaryLive(roomId)
} }
override fun getLocalRoomSummaryLive(roomId: String): LiveData<Optional<LocalRoomSummary>> {
return roomSummaryDataSource.getLocalRoomSummaryLive(roomId)
}
override fun getRoomSummaries( override fun getRoomSummaries(
queryParams: RoomSummaryQueryParams, queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder sortOrder: RoomSortOrder
@ -173,7 +179,10 @@ internal class DefaultRoomService @Inject constructor(
} }
override suspend fun onRoomDisplayed(roomId: String) { override suspend fun onRoomDisplayed(roomId: String) {
updateBreadcrumbsTask.execute(UpdateBreadcrumbsTask.Params(roomId)) // Do not add local rooms to the recent rooms list as they should not be known by the server
if (!RoomLocalEcho.isLocalEchoId(roomId)) {
updateBreadcrumbsTask.execute(UpdateBreadcrumbsTask.Params(roomId))
}
} }
override suspend fun joinRoom(roomIdOrAlias: String, reason: String?, viaServers: List<String>) { override suspend fun joinRoom(roomIdOrAlias: String, reason: String?, viaServers: List<String>) {

View file

@ -17,38 +17,23 @@
package org.matrix.android.sdk.internal.session.room.create package org.matrix.android.sdk.internal.session.room.create
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.kotlin.where
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.whereRoomId import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -56,94 +41,100 @@ import javax.inject.Inject
* Create a room on the server from a local room. * Create a room on the server from a local room.
* The configuration of the local room will be use to configure the new room. * The configuration of the local room will be use to configure the new room.
* The potential local room members will also be invited to this new room. * The potential local room members will also be invited to this new room.
*
* A local tombstone event will be created to indicate that the local room has been replacing by the new one.
*/ */
internal interface CreateRoomFromLocalRoomTask : Task<CreateRoomFromLocalRoomTask.Params, String> { internal interface CreateRoomFromLocalRoomTask : Task<CreateRoomFromLocalRoomTask.Params, String> {
data class Params(val localRoomId: String) data class Params(val localRoomId: String)
} }
internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor(
@UserId private val userId: String,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val createRoomTask: CreateRoomTask, private val createRoomTask: CreateRoomTask,
private val stateEventDataSource: StateEventDataSource, private val roomSummaryDataSource: RoomSummaryDataSource,
private val clock: Clock,
) : CreateRoomFromLocalRoomTask { ) : CreateRoomFromLocalRoomTask {
private val realmConfiguration private val realmConfiguration
get() = monarchy.realmConfiguration get() = monarchy.realmConfiguration
override suspend fun execute(params: CreateRoomFromLocalRoomTask.Params): String { override suspend fun execute(params: CreateRoomFromLocalRoomTask.Params): String {
val replacementRoomId = stateEventDataSource.getStateEvent(params.localRoomId, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty) val localRoomSummary = roomSummaryDataSource.getLocalRoomSummary(params.localRoomId)
?.content.toModel<RoomTombstoneContent>() ?: error("## CreateRoomFromLocalRoomTask - Cannot retrieve LocalRoomSummary with roomId ${params.localRoomId}")
?.replacementRoomId
if (replacementRoomId != null) { // If a room has already been created for the given local room, return the existing roomId
return replacementRoomId if (localRoomSummary.replacementRoomId != null) {
return localRoomSummary.replacementRoomId
} }
var createRoomParams: CreateRoomParams? = null if (localRoomSummary.createRoomParams != null && localRoomSummary.roomSummary != null) {
var isEncrypted = false return createRoom(params.localRoomId, localRoomSummary.roomSummary, localRoomSummary.createRoomParams)
monarchy.doWithRealm { realm -> } else {
realm.where<LocalRoomSummaryEntity>() error("## CreateRoomFromLocalRoomTask - Invalid LocalRoomSummary: $localRoomSummary")
.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, params.localRoomId)
.findFirst()
?.let {
createRoomParams = it.createRoomParams
isEncrypted = it.roomSummaryEntity?.isEncrypted.orFalse()
}
} }
val roomId = createRoomTask.execute(createRoomParams!!) }
/**
* Create a room on the server for the given local room.
*
* @param localRoomId the local room identifier.
* @param localRoomSummary the RoomSummary of the local room.
* @param createRoomParams the CreateRoomParams object which was used to configure the local room.
*
* @return the identifier of the created room.
*/
private suspend fun createRoom(localRoomId: String, localRoomSummary: RoomSummary, createRoomParams: CreateRoomParams): String {
updateCreationState(localRoomId, LocalRoomCreationState.CREATING)
val replacementRoomId = runCatching {
createRoomTask.execute(createRoomParams)
}.fold(
{ it },
{
updateCreationState(localRoomId, LocalRoomCreationState.FAILURE)
throw it
}
)
updateReplacementRoomId(localRoomId, replacementRoomId)
waitForRoomEvents(replacementRoomId, localRoomSummary)
updateCreationState(localRoomId, LocalRoomCreationState.CREATED)
return replacementRoomId
}
/**
* Wait for all the room events before triggering the created state.
*
* @param replacementRoomId the identifier of the created room
* @param localRoomSummary the RoomSummary of the local room.
*/
private suspend fun waitForRoomEvents(replacementRoomId: String, localRoomSummary: RoomSummary) {
try { try {
// Wait for all the room events before triggering the replacement room
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(RoomSummaryEntity::class.java) realm.where(RoomSummaryEntity::class.java)
.equalTo(RoomSummaryEntityFields.ROOM_ID, roomId) .equalTo(RoomSummaryEntityFields.ROOM_ID, replacementRoomId)
.equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, createRoomParams?.invitedUserIds?.size ?: 0) .equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, localRoomSummary.invitedMembersCount)
} }
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
EventEntity.whereRoomId(realm, roomId) EventEntity.whereRoomId(realm, replacementRoomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY) .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_HISTORY_VISIBILITY)
} }
if (isEncrypted) { if (localRoomSummary.isEncrypted) {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
EventEntity.whereRoomId(realm, roomId) EventEntity.whereRoomId(realm, replacementRoomId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION) .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION)
} }
} }
} catch (exception: TimeoutCancellationException) { } catch (exception: TimeoutCancellationException) {
throw CreateRoomFailure.CreatedWithTimeout(roomId) updateCreationState(localRoomSummary.roomId, LocalRoomCreationState.FAILURE)
throw CreateRoomFailure.CreatedWithTimeout(replacementRoomId)
} }
createTombstoneEvent(params, roomId)
return roomId
} }
/** private fun updateCreationState(roomId: String, creationState: LocalRoomCreationState) {
* Create a Tombstone event to indicate that the local room has been replaced by a new one. monarchy.runTransactionSync { realm ->
*/ LocalRoomSummaryEntity.where(realm, roomId).findFirst()?.creationState = creationState
private suspend fun createTombstoneEvent(params: CreateRoomFromLocalRoomTask.Params, roomId: String) { }
val now = clock.epochMillis() }
val event = Event(
type = EventType.STATE_ROOM_TOMBSTONE, private fun updateReplacementRoomId(localRoomId: String, replacementRoomId: String) {
senderId = userId, monarchy.runTransactionSync { realm ->
originServerTs = now, LocalRoomSummaryEntity.where(realm, localRoomId).findFirst()?.replacementRoomId = replacementRoomId
stateKey = "",
eventId = UUID.randomUUID().toString(),
content = RoomTombstoneContent(
replacementRoomId = roomId
).toContent()
)
monarchy.awaitTransaction { realm ->
val eventEntity = event.toEntity(params.localRoomId, SendState.SYNCED, now).copyToRealmOrIgnore(realm, EventInsertType.INCREMENTAL_SYNC)
if (event.stateKey != null && event.type != null && event.eventId != null) {
CurrentStateEventEntity.getOrCreate(realm, params.localRoomId, event.stateKey, event.type).apply {
eventId = event.eventId
root = eventEntity
}
}
} }
} }
} }

View file

@ -22,12 +22,15 @@ import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.database.model.deleteOnCascade
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.whereInRoom
import org.matrix.android.sdk.internal.database.query.whereRoomId import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.room.delete.DeleteLocalRoomTask.Params import org.matrix.android.sdk.internal.session.room.delete.DeleteLocalRoomTask.Params
@ -50,6 +53,12 @@ internal class DefaultDeleteLocalRoomTask @Inject constructor(
if (RoomLocalEcho.isLocalEchoId(roomId)) { if (RoomLocalEcho.isLocalEchoId(roomId)) {
monarchy.awaitTransaction { realm -> monarchy.awaitTransaction { realm ->
Timber.i("## DeleteLocalRoomTask - delete local room id $roomId") Timber.i("## DeleteLocalRoomTask - delete local room id $roomId")
ReadReceiptsSummaryEntity.whereInRoom(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - ReadReceiptsSummaryEntity - delete ${it.size} entries") }
?.deleteAllFromRealm()
ReadReceiptEntity.whereRoomId(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - ReadReceiptEntity - delete ${it.size} entries") }
?.deleteAllFromRealm()
RoomMemberSummaryEntity.where(realm, roomId = roomId).findAll() RoomMemberSummaryEntity.where(realm, roomId = roomId).findAll()
?.also { Timber.i("## DeleteLocalRoomTask - RoomMemberSummaryEntity - delete ${it.size} entries") } ?.also { Timber.i("## DeleteLocalRoomTask - RoomMemberSummaryEntity - delete ${it.size} entries") }
?.deleteAllFromRealm() ?.deleteAllFromRealm()

View file

@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.room.ResultBoundaries
import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.RoomType
@ -43,7 +44,9 @@ import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotification
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.database.mapper.LocalRoomSummaryMapper
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.findByAlias import org.matrix.android.sdk.internal.database.query.findByAlias
@ -57,6 +60,7 @@ import javax.inject.Inject
internal class RoomSummaryDataSource @Inject constructor( internal class RoomSummaryDataSource @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val roomSummaryMapper: RoomSummaryMapper, private val roomSummaryMapper: RoomSummaryMapper,
private val localRoomSummaryMapper: LocalRoomSummaryMapper,
private val queryStringValueProcessor: QueryStringValueProcessor, private val queryStringValueProcessor: QueryStringValueProcessor,
) { ) {
@ -95,6 +99,25 @@ internal class RoomSummaryDataSource @Inject constructor(
) )
} }
fun getLocalRoomSummary(roomId: String): LocalRoomSummary? {
return monarchy
.fetchCopyMap({
LocalRoomSummaryEntity.where(it, roomId).findFirst()
}, { entity, _ ->
localRoomSummaryMapper.map(entity)
})
}
fun getLocalRoomSummaryLive(roomId: String): LiveData<Optional<LocalRoomSummary>> {
val liveData = monarchy.findAllMappedWithChanges(
{ realm -> LocalRoomSummaryEntity.where(realm, roomId) },
{ localRoomSummaryMapper.map(it) }
)
return Transformations.map(liveData) { results ->
results.firstOrNull().toOptional()
}
}
fun getRoomSummariesLive( fun getRoomSummariesLive(
queryParams: RoomSummaryQueryParams, queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE sortOrder: RoomSortOrder = RoomSortOrder.NONE

View file

@ -22,21 +22,22 @@ import io.mockk.coVerify
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.unmockkAll import io.mockk.unmockkAll
import io.mockk.verify
import io.mockk.verifyOrder
import io.realm.kotlin.where import io.realm.kotlin.where
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeNull
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntity
@ -44,29 +45,24 @@ import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.util.time.DefaultClock
import org.matrix.android.sdk.test.fakes.FakeMonarchy import org.matrix.android.sdk.test.fakes.FakeMonarchy
import org.matrix.android.sdk.test.fakes.FakeStateEventDataSource import org.matrix.android.sdk.test.fakes.FakeRoomSummaryDataSource
private const val A_LOCAL_ROOM_ID = "local.a-local-room-id" private const val A_LOCAL_ROOM_ID = "local.a-local-room-id"
private const val AN_EXISTING_ROOM_ID = "an-existing-room-id" private const val AN_EXISTING_ROOM_ID = "an-existing-room-id"
private const val A_ROOM_ID = "a-room-id" private const val A_ROOM_ID = "a-room-id"
private const val MY_USER_ID = "my-user-id"
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
internal class DefaultCreateRoomFromLocalRoomTaskTest { internal class DefaultCreateRoomFromLocalRoomTaskTest {
private val fakeMonarchy = FakeMonarchy() private val fakeMonarchy = FakeMonarchy()
private val clock = DefaultClock()
private val createRoomTask = mockk<CreateRoomTask>() private val createRoomTask = mockk<CreateRoomTask>()
private val fakeStateEventDataSource = FakeStateEventDataSource() private val fakeRoomSummaryDataSource = FakeRoomSummaryDataSource()
private val defaultCreateRoomFromLocalRoomTask = DefaultCreateRoomFromLocalRoomTask( private val defaultCreateRoomFromLocalRoomTask = DefaultCreateRoomFromLocalRoomTask(
userId = MY_USER_ID,
monarchy = fakeMonarchy.instance, monarchy = fakeMonarchy.instance,
createRoomTask = createRoomTask, createRoomTask = createRoomTask,
stateEventDataSource = fakeStateEventDataSource.instance, roomSummaryDataSource = fakeRoomSummaryDataSource.instance,
clock = clock
) )
@Before @Before
@ -91,13 +87,12 @@ internal class DefaultCreateRoomFromLocalRoomTaskTest {
@Test @Test
fun `given a local room id when execute then the existing room id is kept`() = runTest { fun `given a local room id when execute then the existing room id is kept`() = runTest {
// Given // Given
givenATombstoneEvent( val aCreateRoomParams = mockk<CreateRoomParams>(relaxed = true)
Event( givenALocalRoomSummary(aCreateRoomParams = aCreateRoomParams, aCreationState = LocalRoomCreationState.CREATED, aReplacementRoomId = AN_EXISTING_ROOM_ID)
roomId = A_LOCAL_ROOM_ID, val aLocalRoomSummaryEntity = givenALocalRoomSummaryEntity(
type = EventType.STATE_ROOM_TOMBSTONE, aCreateRoomParams = aCreateRoomParams,
stateKey = "", aCreationState = LocalRoomCreationState.CREATED,
content = RoomTombstoneContent(replacementRoomId = AN_EXISTING_ROOM_ID).toContent() aReplacementRoomId = AN_EXISTING_ROOM_ID
)
) )
// When // When
@ -105,20 +100,18 @@ internal class DefaultCreateRoomFromLocalRoomTaskTest {
val result = defaultCreateRoomFromLocalRoomTask.execute(params) val result = defaultCreateRoomFromLocalRoomTask.execute(params)
// Then // Then
verifyTombstoneEvent(AN_EXISTING_ROOM_ID) fakeRoomSummaryDataSource.verifyGetLocalRoomSummary(A_LOCAL_ROOM_ID)
result shouldBeEqualTo AN_EXISTING_ROOM_ID result shouldBeEqualTo AN_EXISTING_ROOM_ID
aLocalRoomSummaryEntity.replacementRoomId shouldBeEqualTo AN_EXISTING_ROOM_ID
aLocalRoomSummaryEntity.creationState shouldBeEqualTo LocalRoomCreationState.CREATED
} }
@Test @Test
fun `given a local room id when execute then it is correctly executed`() = runTest { fun `given a local room id when execute then it is correctly executed`() = runTest {
// Given // Given
val aCreateRoomParams = mockk<CreateRoomParams>() val aCreateRoomParams = mockk<CreateRoomParams>(relaxed = true)
val aLocalRoomSummaryEntity = mockk<LocalRoomSummaryEntity> { givenALocalRoomSummary(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
every { roomSummaryEntity } returns mockk(relaxed = true) val aLocalRoomSummaryEntity = givenALocalRoomSummaryEntity(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
every { createRoomParams } returns aCreateRoomParams
}
givenATombstoneEvent(null)
givenALocalRoomSummaryEntity(aLocalRoomSummaryEntity)
coEvery { createRoomTask.execute(any()) } returns A_ROOM_ID coEvery { createRoomTask.execute(any()) } returns A_ROOM_ID
@ -127,32 +120,84 @@ internal class DefaultCreateRoomFromLocalRoomTaskTest {
val result = defaultCreateRoomFromLocalRoomTask.execute(params) val result = defaultCreateRoomFromLocalRoomTask.execute(params)
// Then // Then
verifyTombstoneEvent(null) fakeRoomSummaryDataSource.verifyGetLocalRoomSummary(A_LOCAL_ROOM_ID)
// CreateRoomTask has been called with the initial CreateRoomParams // CreateRoomTask has been called with the initial CreateRoomParams
coVerify { createRoomTask.execute(aCreateRoomParams) } coVerify { createRoomTask.execute(aCreateRoomParams) }
// The resulting roomId matches the roomId returned by the createRoomTask // The resulting roomId matches the roomId returned by the createRoomTask
result shouldBeEqualTo A_ROOM_ID result shouldBeEqualTo A_ROOM_ID
// A tombstone state event has been created // The room creation state has correctly been updated
coVerify { CurrentStateEventEntity.getOrCreate(realm = any(), roomId = A_LOCAL_ROOM_ID, stateKey = any(), type = EventType.STATE_ROOM_TOMBSTONE) } verifyOrder {
aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.CREATING
aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.CREATED
}
// The local room summary has been updated with the created room id
verify { aLocalRoomSummaryEntity.replacementRoomId = A_ROOM_ID }
aLocalRoomSummaryEntity.replacementRoomId shouldBeEqualTo A_ROOM_ID
aLocalRoomSummaryEntity.creationState shouldBeEqualTo LocalRoomCreationState.CREATED
} }
private fun givenATombstoneEvent(event: Event?) { @Test
fakeStateEventDataSource.givenGetStateEventReturns(event) fun `given a local room id when execute with an exception then the creation state is correctly updated`() = runTest {
// Given
val aCreateRoomParams = mockk<CreateRoomParams>(relaxed = true)
givenALocalRoomSummary(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
val aLocalRoomSummaryEntity = givenALocalRoomSummaryEntity(aCreateRoomParams = aCreateRoomParams, aReplacementRoomId = null)
coEvery { createRoomTask.execute(any()) }.throws(mockk())
// When
val params = CreateRoomFromLocalRoomTask.Params(A_LOCAL_ROOM_ID)
tryOrNull { defaultCreateRoomFromLocalRoomTask.execute(params) }
// Then
fakeRoomSummaryDataSource.verifyGetLocalRoomSummary(A_LOCAL_ROOM_ID)
// CreateRoomTask has been called with the initial CreateRoomParams
coVerify { createRoomTask.execute(aCreateRoomParams) }
// The room creation state has correctly been updated
verifyOrder {
aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.CREATING
aLocalRoomSummaryEntity.creationState = LocalRoomCreationState.FAILURE
}
// The local room summary has been updated with the created room id
aLocalRoomSummaryEntity.replacementRoomId.shouldBeNull()
aLocalRoomSummaryEntity.creationState shouldBeEqualTo LocalRoomCreationState.FAILURE
} }
private fun givenALocalRoomSummaryEntity(localRoomSummaryEntity: LocalRoomSummaryEntity) { private fun givenALocalRoomSummary(
aCreateRoomParams: CreateRoomParams,
aCreationState: LocalRoomCreationState = LocalRoomCreationState.NOT_CREATED,
aReplacementRoomId: String? = null
): LocalRoomSummary {
val aLocalRoomSummary = LocalRoomSummary(
roomId = A_LOCAL_ROOM_ID,
roomSummary = mockk(relaxed = true),
createRoomParams = aCreateRoomParams,
creationState = aCreationState,
replacementRoomId = aReplacementRoomId,
)
fakeRoomSummaryDataSource.givenGetLocalRoomSummaryReturns(A_LOCAL_ROOM_ID, aLocalRoomSummary)
return aLocalRoomSummary
}
private fun givenALocalRoomSummaryEntity(
aCreateRoomParams: CreateRoomParams,
aCreationState: LocalRoomCreationState = LocalRoomCreationState.NOT_CREATED,
aReplacementRoomId: String? = null
): LocalRoomSummaryEntity {
val aLocalRoomSummaryEntity = spyk(LocalRoomSummaryEntity(
roomId = A_LOCAL_ROOM_ID,
roomSummaryEntity = mockk(relaxed = true),
replacementRoomId = aReplacementRoomId,
).apply {
createRoomParams = aCreateRoomParams
creationState = aCreationState
})
every { every {
fakeMonarchy.fakeRealm.instance fakeMonarchy.fakeRealm.instance
.where<LocalRoomSummaryEntity>() .where<LocalRoomSummaryEntity>()
.equalTo(LocalRoomSummaryEntityFields.ROOM_ID, A_LOCAL_ROOM_ID) .equalTo(LocalRoomSummaryEntityFields.ROOM_ID, A_LOCAL_ROOM_ID)
.findFirst() .findFirst()
} returns localRoomSummaryEntity } returns aLocalRoomSummaryEntity
} return aLocalRoomSummaryEntity
private fun verifyTombstoneEvent(expectedRoomId: String?) {
fakeStateEventDataSource.verifyGetStateEvent(A_LOCAL_ROOM_ID, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)
fakeStateEventDataSource.instance.getStateEvent(A_LOCAL_ROOM_ID, EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)
?.content.toModel<RoomTombstoneContent>()
?.replacementRoomId shouldBeEqualTo expectedRoomId
} }
} }

View file

@ -47,6 +47,11 @@ internal class FakeMonarchy {
} coAnswers { } coAnswers {
firstArg<Monarchy.RealmBlock>().doWithRealm(fakeRealm.instance) firstArg<Monarchy.RealmBlock>().doWithRealm(fakeRealm.instance)
} }
coEvery {
instance.runTransactionSync(any())
} coAnswers {
firstArg<Realm.Transaction>().execute(fakeRealm.instance)
}
every { instance.realmConfiguration } returns mockk() every { instance.realmConfiguration } returns mockk()
} }

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.test.fakes
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.matrix.android.sdk.api.session.room.model.LocalRoomSummary
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
internal class FakeRoomSummaryDataSource {
val instance: RoomSummaryDataSource = mockk()
fun givenGetLocalRoomSummaryReturns(roomId: String?, localRoomSummary: LocalRoomSummary?) {
every { instance.getLocalRoomSummary(roomId = roomId ?: any()) } returns localRoomSummary
}
fun verifyGetLocalRoomSummary(roomId: String) {
verify { instance.getLocalRoomSummary(roomId) }
}
}

File diff suppressed because it is too large Load diff

View file

@ -370,9 +370,9 @@ dependencies {
debugImplementation 'com.facebook.soloader:soloader:0.10.4' debugImplementation 'com.facebook.soloader:soloader:0.10.4'
debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0" debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
gplayImplementation "com.google.android.gms:play-services-location:16.0.0" gplayImplementation "com.google.android.gms:play-services-location:20.0.0"
// UnifiedPush gplay flavor only // UnifiedPush gplay flavor only
gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.2') { gplayImplementation('com.google.firebase:firebase-messaging:23.0.8') {
exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'

View file

@ -225,8 +225,8 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
// Wait until local secrets are known (gossip) // Wait until local secrets are known (gossip)
withIdlingResource(allSecretsKnownIdling(uiSession)) { withIdlingResource(allSecretsKnownIdling(uiSession)) {
onView(withId(R.id.groupToolbarAvatarImageView)) onView(withId(R.id.roomListContainer))
.perform(click()) .check(matches(isDisplayed()))
} }
} }

View file

@ -50,7 +50,7 @@ import im.vector.app.withIdlingResource
import timber.log.Timber import timber.log.Timber
class ElementRobot( class ElementRobot(
private val labsPreferences: LabFeaturesPreferences = LabFeaturesPreferences(false) private val labsPreferences: LabFeaturesPreferences = LabFeaturesPreferences(true)
) { ) {
fun onboarding(block: OnboardingRobot.() -> Unit) { fun onboarding(block: OnboardingRobot.() -> Unit) {
block(OnboardingRobot()) block(OnboardingRobot())
@ -110,9 +110,6 @@ class ElementRobot(
closeSoftKeyboard() closeSoftKeyboard()
block(NewDirectMessageRobot()) block(NewDirectMessageRobot())
pressBack() pressBack()
if (labsPreferences.isNewAppLayoutEnabled) {
pressBack() // close create dialog
}
waitUntilViewVisible(withId(R.id.roomListContainer)) waitUntilViewVisible(withId(R.id.roomListContainer))
} }
@ -121,9 +118,6 @@ class ElementRobot(
clickOn(R.id.bottom_action_rooms) clickOn(R.id.bottom_action_rooms)
} }
RoomListRobot(labsPreferences).newRoom { block() } RoomListRobot(labsPreferences).newRoom { block() }
if (labsPreferences.isNewAppLayoutEnabled) {
pressBack() // close create dialog
}
waitUntilViewVisible(withId(R.id.roomListContainer)) waitUntilViewVisible(withId(R.id.roomListContainer))
} }

View file

@ -80,11 +80,6 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.forceUsageOfOpusEncoder, key = DebugFeatureKeys.forceUsageOfOpusEncoder,
factory = VectorFeatures::forceUsageOfOpusEncoder factory = VectorFeatures::forceUsageOfOpusEncoder
), ),
createBooleanFeature(
label = "Start DM on first message",
key = DebugFeatureKeys.startDmOnFirstMsg,
factory = VectorFeatures::shouldStartDmOnFirstMessage
),
createBooleanFeature( createBooleanFeature(
label = "Enable New App Layout", label = "Enable New App Layout",
key = DebugFeatureKeys.newAppLayoutEnabled, key = DebugFeatureKeys.newAppLayoutEnabled,

View file

@ -73,9 +73,6 @@ class DebugVectorFeatures(
override fun forceUsageOfOpusEncoder(): Boolean = read(DebugFeatureKeys.forceUsageOfOpusEncoder) override fun forceUsageOfOpusEncoder(): Boolean = read(DebugFeatureKeys.forceUsageOfOpusEncoder)
?: vectorFeatures.forceUsageOfOpusEncoder() ?: vectorFeatures.forceUsageOfOpusEncoder()
override fun shouldStartDmOnFirstMessage(): Boolean = read(DebugFeatureKeys.startDmOnFirstMsg)
?: vectorFeatures.shouldStartDmOnFirstMessage()
override fun isNewAppLayoutFeatureEnabled(): Boolean = read(DebugFeatureKeys.newAppLayoutEnabled) override fun isNewAppLayoutFeatureEnabled(): Boolean = read(DebugFeatureKeys.newAppLayoutEnabled)
?: vectorFeatures.isNewAppLayoutFeatureEnabled() ?: vectorFeatures.isNewAppLayoutFeatureEnabled()

View file

@ -37,8 +37,10 @@
<bool name="settings_ignored_users_visible">true</bool> <bool name="settings_ignored_users_visible">true</bool>
<!-- Level 1: Labs --> <!-- Level 1: Labs -->
<bool name="settings_labs_deferred_dm_visible">true</bool>
<bool name="settings_labs_deferred_dm_default">true</bool>
<bool name="settings_labs_thread_messages_default">false</bool> <bool name="settings_labs_thread_messages_default">false</bool>
<bool name="settings_labs_new_app_layout_default">false</bool> <bool name="settings_labs_new_app_layout_default">true</bool>
<bool name="settings_timeline_show_live_sender_info_visible">true</bool> <bool name="settings_timeline_show_live_sender_info_visible">true</bool>
<bool name="settings_timeline_show_live_sender_info_default">false</bool> <bool name="settings_timeline_show_live_sender_info_default">false</bool>
<!-- Level 1: Advanced settings --> <!-- Level 1: Advanced settings -->

View file

@ -323,6 +323,7 @@
<activity android:name=".features.home.room.list.home.invites.InvitesActivity" /> <activity android:name=".features.home.room.list.home.invites.InvitesActivity" />
<activity android:name=".features.home.room.list.home.release.ReleaseNotesActivity" /> <activity android:name=".features.home.room.list.home.release.ReleaseNotesActivity" />
<activity android:name=".features.settings.devices.v2.overview.SessionOverviewActivity" /> <activity android:name=".features.settings.devices.v2.overview.SessionOverviewActivity" />
<activity android:name=".features.settings.devices.v2.othersessions.OtherSessionsActivity" />
<activity android:name=".features.settings.devices.v2.details.SessionDetailsActivity" /> <activity android:name=".features.settings.devices.v2.details.SessionDetailsActivity" />
<!-- Services --> <!-- Services -->

View file

@ -89,6 +89,7 @@ import im.vector.app.features.settings.crosssigning.CrossSigningSettingsViewMode
import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheetViewModel import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheetViewModel
import im.vector.app.features.settings.devices.DevicesViewModel import im.vector.app.features.settings.devices.DevicesViewModel
import im.vector.app.features.settings.devices.v2.details.SessionDetailsViewModel import im.vector.app.features.settings.devices.v2.details.SessionDetailsViewModel
import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsViewModel
import im.vector.app.features.settings.devices.v2.overview.SessionOverviewViewModel import im.vector.app.features.settings.devices.v2.overview.SessionOverviewViewModel
import im.vector.app.features.settings.devtools.AccountDataViewModel import im.vector.app.features.settings.devtools.AccountDataViewModel
import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailViewModel import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailViewModel
@ -643,6 +644,11 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(SessionOverviewViewModel::class) @MavericksViewModelKey(SessionOverviewViewModel::class)
fun sessionOverviewViewModelFactory(factory: SessionOverviewViewModel.Factory): MavericksAssistedViewModelFactory<*, *> fun sessionOverviewViewModelFactory(factory: SessionOverviewViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(OtherSessionsViewModel::class)
fun otherSessionsViewModelFactory(factory: OtherSessionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds @Binds
@IntoMap @IntoMap
@MavericksViewModelKey(SessionDetailsViewModel::class) @MavericksViewModelKey(SessionDetailsViewModel::class)

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.utils
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
/**
* This observer detects when item was added or moved to the first position of the adapter, while recyclerView is scrolled to the top. This is necessary
* to force recycler to scroll to the top to make such item visible, because by default it will keep items on screen, while adding new item to the top,
* outside of the viewport
* @param layoutManager - [LinearLayoutManager] of the recycler view, which displays items
* @property onItemUpdated - callback to be called, when observer detects event
*/
class FirstItemUpdatedObserver(
layoutManager: LinearLayoutManager,
private val onItemUpdated: () -> Unit
) : RecyclerView.AdapterDataObserver() {
val layoutManager: LinearLayoutManager? by weak(layoutManager)
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
if ((toPosition == 0 || fromPosition == 0) && layoutManager?.findFirstCompletelyVisibleItemPosition() == 0) {
onItemUpdated.invoke()
}
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0 && layoutManager?.findFirstCompletelyVisibleItemPosition() == 0) {
onItemUpdated.invoke()
}
}
}

View file

@ -33,7 +33,6 @@ interface VectorFeatures {
fun isScreenSharingEnabled(): Boolean fun isScreenSharingEnabled(): Boolean
fun isLocationSharingEnabled(): Boolean fun isLocationSharingEnabled(): Boolean
fun forceUsageOfOpusEncoder(): Boolean fun forceUsageOfOpusEncoder(): Boolean
fun shouldStartDmOnFirstMessage(): Boolean
/** /**
* This is only to enable if the labs flag should be visible and effective. * This is only to enable if the labs flag should be visible and effective.
@ -56,7 +55,6 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isScreenSharingEnabled(): Boolean = true override fun isScreenSharingEnabled(): Boolean = true
override fun isLocationSharingEnabled() = Config.ENABLE_LOCATION_SHARING override fun isLocationSharingEnabled() = Config.ENABLE_LOCATION_SHARING
override fun forceUsageOfOpusEncoder(): Boolean = false override fun forceUsageOfOpusEncoder(): Boolean = false
override fun shouldStartDmOnFirstMessage(): Boolean = false
override fun isNewAppLayoutFeatureEnabled(): Boolean = true override fun isNewAppLayoutFeatureEnabled(): Boolean = true
override fun isNewDeviceManagementEnabled(): Boolean = false override fun isNewDeviceManagementEnabled(): Boolean = false
} }

View file

@ -26,11 +26,11 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.CreatedRoom import im.vector.app.features.analytics.plan.CreatedRoom
import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault import im.vector.app.features.raw.wellknown.isE2EByDefault
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.userdirectory.PendingSelection import im.vector.app.features.userdirectory.PendingSelection
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -45,9 +45,9 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
class CreateDirectRoomViewModel @AssistedInject constructor( class CreateDirectRoomViewModel @AssistedInject constructor(
@Assisted initialState: CreateDirectRoomViewState, @Assisted initialState: CreateDirectRoomViewState,
private val rawService: RawService, private val rawService: RawService,
private val vectorPreferences: VectorPreferences,
val session: Session, val session: Session,
val analyticsTracker: AnalyticsTracker, val analyticsTracker: AnalyticsTracker,
val vectorFeatures: VectorFeatures
) : ) :
VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomAction, CreateDirectRoomViewEvents>(initialState) { VectorViewModel<CreateDirectRoomViewState, CreateDirectRoomAction, CreateDirectRoomViewEvents>(initialState) {
@ -124,7 +124,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(
} }
val result = runCatchingToAsync { val result = runCatchingToAsync {
if (vectorFeatures.shouldStartDmOnFirstMessage()) { if (vectorPreferences.isDeferredDmEnabled()) {
session.roomService().createLocalRoom(roomParams) session.roomService().createLocalRoom(roomParams)
} else { } else {
analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse())) analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse()))

View file

@ -16,11 +16,11 @@
package im.vector.app.features.createdirect package im.vector.app.features.createdirect
import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.CreatedRoom import im.vector.app.features.analytics.plan.CreatedRoom
import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault import im.vector.app.features.raw.wellknown.isE2EByDefault
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.raw.RawService
@ -32,7 +32,7 @@ class DirectRoomHelper @Inject constructor(
private val rawService: RawService, private val rawService: RawService,
private val session: Session, private val session: Session,
private val analyticsTracker: AnalyticsTracker, private val analyticsTracker: AnalyticsTracker,
private val vectorFeatures: VectorFeatures, private val vectorPreferences: VectorPreferences,
) { ) {
suspend fun ensureDMExists(userId: String): String { suspend fun ensureDMExists(userId: String): String {
@ -50,7 +50,7 @@ class DirectRoomHelper @Inject constructor(
setDirectMessage() setDirectMessage()
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault
} }
roomId = if (vectorFeatures.shouldStartDmOnFirstMessage()) { roomId = if (vectorPreferences.isDeferredDmEnabled()) {
session.roomService().createLocalRoom(roomParams) session.roomService().createLocalRoom(roomParams)
} else { } else {
analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse())) analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse()))

View file

@ -84,6 +84,7 @@ import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.spaces.share.ShareSpaceBottomSheet import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.usercode.UserCodeActivity
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -634,10 +635,18 @@ class HomeActivity :
launchInviteFriends() launchInviteFriends()
true true
} }
R.id.menu_home_qr -> {
launchQrCode()
true
}
else -> false else -> false
} }
} }
private fun launchQrCode() {
startActivity(UserCodeActivity.newIntent(this, sharedActionViewModel.session.myUserId))
}
private fun launchInviteFriends() { private fun launchInviteFriends() {
activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink -> activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink ->
analyticsTracker.screen(MobileScreen(screenName = MobileScreen.ScreenName.InviteFriends)) analyticsTracker.screen(MobileScreen(screenName = MobileScreen.ScreenName.InviteFriends))

View file

@ -119,17 +119,19 @@ class HomeActivityViewModel @AssistedInject constructor(
} }
private fun observeReleaseNotes() = withState { state -> private fun observeReleaseNotes() = withState { state ->
// we don't want to show release notes for new users or after relogin if (vectorPreferences.isNewAppLayoutEnabled()) {
if (state.authenticationDescription == null && vectorPreferences.isNewAppLayoutEnabled()) { // we don't want to show release notes for new users or after relogin
releaseNotesPreferencesStore.appLayoutOnboardingShown.onEach { isAppLayoutOnboardingShown -> if (state.authenticationDescription == null) {
if (!isAppLayoutOnboardingShown) { releaseNotesPreferencesStore.appLayoutOnboardingShown.onEach { isAppLayoutOnboardingShown ->
_viewEvents.post(HomeActivityViewEvents.ShowReleaseNotes) if (!isAppLayoutOnboardingShown) {
_viewEvents.post(HomeActivityViewEvents.ShowReleaseNotes)
}
}.launchIn(viewModelScope)
} else {
// we assume that users which came from auth flow either have seen updates already (relogin) or don't need them (new user)
viewModelScope.launch {
releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true)
} }
}.launchIn(viewModelScope)
} else {
// we assume that users which came from auth flow either have seen updates already (relogin) or don't need them (new user)
viewModelScope.launch {
releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true)
} }
} }
} }

View file

@ -51,7 +51,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
object OpenRoomProfile : RoomDetailViewEvents() object OpenRoomProfile : RoomDetailViewEvents()
data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents() data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents()
object ShowWaitingView : RoomDetailViewEvents() data class ShowWaitingView(val text: String? = null) : RoomDetailViewEvents()
object HideWaitingView : RoomDetailViewEvents() object HideWaitingView : RoomDetailViewEvents()
data class DownloadFileState( data class DownloadFileState(

View file

@ -493,7 +493,7 @@ class TimelineFragment :
is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message) is RoomDetailViewEvents.ShowInfoOkDialog -> showDialogWithMessage(it.message)
is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo) is RoomDetailViewEvents.JoinJitsiConference -> joinJitsiRoom(it.widget, it.withVideo)
RoomDetailViewEvents.LeaveJitsiConference -> leaveJitsiConference() RoomDetailViewEvents.LeaveJitsiConference -> leaveJitsiConference()
RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView() is RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView(it.text)
RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView() RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it) is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)

View file

@ -39,6 +39,7 @@ import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.DecryptionFailureTracker import im.vector.app.features.analytics.DecryptionFailureTracker
import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom
import im.vector.app.features.analytics.plan.CreatedRoom
import im.vector.app.features.analytics.plan.JoinedRoom import im.vector.app.features.analytics.plan.JoinedRoom
import im.vector.app.features.call.conference.ConferenceEvent import im.vector.app.features.call.conference.ConferenceEvent
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
@ -78,12 +79,12 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.MatrixPatterns
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.RelationType
@ -100,9 +101,11 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.LocalRoomCreationState
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
@ -185,6 +188,7 @@ class TimelineViewModel @AssistedInject constructor(
init { init {
// This method will take care of a null room to update the state. // This method will take care of a null room to update the state.
observeRoomSummary() observeRoomSummary()
observeLocalRoomSummary()
if (room == null) { if (room == null) {
timeline = null timeline = null
} else { } else {
@ -617,7 +621,7 @@ class TimelineViewModel @AssistedInject constructor(
} }
private fun handleAddJitsiConference(action: RoomDetailAction.AddJitsiWidget) { private fun handleAddJitsiConference(action: RoomDetailAction.AddJitsiWidget) {
_viewEvents.post(RoomDetailViewEvents.ShowWaitingView) _viewEvents.post(RoomDetailViewEvents.ShowWaitingView())
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
val widget = jitsiService.createJitsiWidget(initialState.roomId, action.withVideo) val widget = jitsiService.createJitsiWidget(initialState.roomId, action.withVideo)
@ -637,7 +641,7 @@ class TimelineViewModel @AssistedInject constructor(
if (isJitsiWidget) { if (isJitsiWidget) {
setState { copy(jitsiState = jitsiState.copy(deleteWidgetInProgress = true)) } setState { copy(jitsiState = jitsiState.copy(deleteWidgetInProgress = true)) }
} else { } else {
_viewEvents.post(RoomDetailViewEvents.ShowWaitingView) _viewEvents.post(RoomDetailViewEvents.ShowWaitingView())
} }
session.widgetService().destroyRoomWidget(initialState.roomId, widgetId) session.widgetService().destroyRoomWidget(initialState.roomId, widgetId)
// local echo // local echo
@ -1231,6 +1235,32 @@ class TimelineViewModel @AssistedInject constructor(
} }
} }
private fun observeLocalRoomSummary() {
if (room != null && RoomLocalEcho.isLocalEchoId(room.roomId)) {
room.flow().liveLocalRoomSummary()
.unwrap()
.map { it.creationState }
.distinctUntilChanged()
.onEach { creationState ->
when (creationState) {
LocalRoomCreationState.NOT_CREATED -> Unit
LocalRoomCreationState.CREATING ->
_viewEvents.post(RoomDetailViewEvents.ShowWaitingView(stringProvider.getString(R.string.creating_direct_room)))
LocalRoomCreationState.FAILURE -> {
_viewEvents.post(RoomDetailViewEvents.HideWaitingView)
}
LocalRoomCreationState.CREATED -> {
room.localRoomSummary()?.let {
analyticsTracker.capture(CreatedRoom(isDM = it.roomSummary?.isDirect.orFalse()))
_viewEvents.post(RoomDetailViewEvents.OpenRoom(it.replacementRoomId!!, true))
}
}
}
}
.launchIn(viewModelScope)
}
}
private fun getUnreadState() { private fun getUnreadState() {
if (room == null) return if (room == null) return
combine( combine(
@ -1322,26 +1352,11 @@ class TimelineViewModel @AssistedInject constructor(
} }
} }
room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)?.also { room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE, QueryStringValue.IsEmpty)?.also {
onRoomTombstoneUpdated(it) setState { copy(tombstoneEvent = it) }
} }
} }
} }
private var roomTombstoneHandled = false
private fun onRoomTombstoneUpdated(tombstoneEvent: Event) = withState { state ->
if (roomTombstoneHandled) return@withState
if (state.isLocalRoom()) {
// Local room has been replaced, so navigate to the new room
val roomId = tombstoneEvent.getClearContent()?.toModel<RoomTombstoneContent>()
?.replacementRoomId
?: return@withState
_viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId, closeCurrentRoom = true))
roomTombstoneHandled = true
} else {
setState { copy(tombstoneEvent = tombstoneEvent) }
}
}
/** /**
* Navigates to the appropriate event (by paginating the thread timeline until the event is found * Navigates to the appropriate event (by paginating the thread timeline until the event is found
* in the snapshot. The main reason for this function is to support the /relations api * in the snapshot. The main reason for this function is to support the /relations api

View file

@ -31,4 +31,5 @@ sealed class RoomListAction : VectorViewModelAction {
data class LeaveRoom(val roomId: String) : RoomListAction() data class LeaveRoom(val roomId: String) : RoomListAction()
data class JoinSuggestedRoom(val roomId: String, val viaServers: List<String>?) : RoomListAction() data class JoinSuggestedRoom(val roomId: String, val viaServers: List<String>?) : RoomListAction()
data class ShowRoomDetails(val roomId: String, val viaServers: List<String>?) : RoomListAction() data class ShowRoomDetails(val roomId: String, val viaServers: List<String>?) : RoomListAction()
object DeleteAllLocalRoom : RoomListAction()
} }

View file

@ -149,10 +149,13 @@ class RoomListFragment :
(it.contentEpoxyController as? RoomSummaryPagedController)?.roomChangeMembershipStates = ms (it.contentEpoxyController as? RoomSummaryPagedController)?.roomChangeMembershipStates = ms
} }
} }
roomListViewModel.onEach(RoomListViewState::localRoomIds) { }
// Local rooms should not exist anymore when the room list is shown
roomListViewModel.deleteLocalRooms(it) override fun onStart() {
} super.onStart()
// Local rooms should not exist anymore when the room list is shown
roomListViewModel.handle(RoomListAction.DeleteAllLocalRoom)
} }
private fun refreshCollapseStates() { private fun refreshCollapseStates() {

View file

@ -97,7 +97,6 @@ class RoomListViewModel @AssistedInject constructor(
init { init {
observeMembershipChanges() observeMembershipChanges()
observeLocalRooms()
spaceStateHandler.getSelectedSpaceFlow() spaceStateHandler.getSelectedSpaceFlow()
.distinctUntilChanged() .distinctUntilChanged()
@ -125,16 +124,6 @@ class RoomListViewModel @AssistedInject constructor(
} }
} }
private fun observeLocalRooms() {
session
.flow()
.liveRoomSummaries(roomSummaryQueryParams {
roomId = QueryStringValue.Contains(RoomLocalEcho.PREFIX)
})
.map { page -> page.map { it.roomId } }
.setOnEach { copy(localRoomIds = it) }
}
companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by hiltMavericksViewModelFactory() companion object : MavericksViewModelFactory<RoomListViewModel, RoomListViewState> by hiltMavericksViewModelFactory()
private val roomListSectionBuilder = RoomListSectionBuilder( private val roomListSectionBuilder = RoomListSectionBuilder(
@ -166,6 +155,7 @@ class RoomListViewModel @AssistedInject constructor(
is RoomListAction.ToggleSection -> handleToggleSection(action.section) is RoomListAction.ToggleSection -> handleToggleSection(action.section)
is RoomListAction.JoinSuggestedRoom -> handleJoinSuggestedRoom(action) is RoomListAction.JoinSuggestedRoom -> handleJoinSuggestedRoom(action)
is RoomListAction.ShowRoomDetails -> handleShowRoomDetails(action) is RoomListAction.ShowRoomDetails -> handleShowRoomDetails(action)
RoomListAction.DeleteAllLocalRoom -> handleDeleteLocalRooms()
} }
} }
@ -173,14 +163,6 @@ class RoomListViewModel @AssistedInject constructor(
return session.getRoom(roomId)?.stateService()?.isPublic().orFalse() return session.getRoom(roomId)?.stateService()?.isPublic().orFalse()
} }
fun deleteLocalRooms(roomsIds: Iterable<String>) {
viewModelScope.launch {
roomsIds.forEach {
session.roomService().deleteLocalRoom(it)
}
}
}
// PRIVATE METHODS ***************************************************************************** // PRIVATE METHODS *****************************************************************************
private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState { private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState {
@ -338,4 +320,16 @@ class RoomListViewModel @AssistedInject constructor(
_viewEvents.post(value) _viewEvents.post(value)
} }
} }
private fun handleDeleteLocalRooms() {
val localRoomIds = session.roomService()
.getRoomSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Contains(RoomLocalEcho.PREFIX) })
.map { it.roomId }
viewModelScope.launch {
localRoomIds.forEach {
session.roomService().deleteLocalRoom(it)
}
}
}
} }

View file

@ -31,7 +31,6 @@ data class RoomListViewState(
val asyncSuggestedRooms: Async<List<SpaceChildInfo>> = Uninitialized, val asyncSuggestedRooms: Async<List<SpaceChildInfo>> = Uninitialized,
val currentUserName: String? = null, val currentUserName: String? = null,
val asyncSelectedSpace: Async<RoomSummary?> = Uninitialized, val asyncSelectedSpace: Async<RoomSummary?> = Uninitialized,
val localRoomIds: List<String> = emptyList()
) : MavericksState { ) : MavericksState {
constructor(args: RoomListParams) : this(displayMode = args.displayMode) constructor(args: RoomListParams) : this(displayMode = args.displayMode)

View file

@ -103,6 +103,9 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>(R.layo
@EpoxyAttribute @EpoxyAttribute
var showSelected: Boolean = false var showSelected: Boolean = false
@EpoxyAttribute
var useSingleLineForLastEvent: Boolean = false
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
@ -122,6 +125,10 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>(R.layo
holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending
renderSelection(holder, showSelected) renderSelection(holder, showSelected)
holder.roomAvatarPresenceImageView.render(showPresence, userPresence) holder.roomAvatarPresenceImageView.render(showPresence, userPresence)
if (useSingleLineForLastEvent) {
holder.subtitleView.setLines(1)
}
} }
private fun renderDisplayMode(holder: Holder) = when (displayMode) { private fun renderDisplayMode(holder: Holder) = when (displayMode) {

Some files were not shown because too many files have changed in this diff Show more