mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-28 14:08:44 +03:00
Merge tag 'v1.1.16' into sc
v1.1.16 Change-Id: Id68bd7e36129c9168cc871d81c5c0935b689e1e9 Conflicts: matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt
This commit is contained in:
commit
677330921d
120 changed files with 2882 additions and 395 deletions
15
CHANGES.md
15
CHANGES.md
|
@ -1,3 +1,18 @@
|
|||
Changes in Element v1.1.16 (2021-08-09)
|
||||
=======================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Spaces - Support Restricted Room via room capabilities API ([#3509](https://github.com/vector-im/element-android/issues/3509))
|
||||
- Spaces | Support restricted room access in room settings ([#3665](https://github.com/vector-im/element-android/issues/3665))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Fix crash when opening Troubleshoot Notifications ([#3778](https://github.com/vector-im/element-android/issues/3778))
|
||||
- Fix error when sending encrypted message if someone in the room logs out. ([#3792](https://github.com/vector-im/element-android/issues/3792))
|
||||
- Voice Message - Amplitude update java.util.ConcurrentModificationException ([#3796](https://github.com/vector-im/element-android/issues/3796))
|
||||
|
||||
|
||||
Changes in Element v1.1.15 (2021-07-30)
|
||||
=======================================
|
||||
|
||||
|
|
2
fastlane/metadata/android/cs-CZ/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: aktualizace hlavně kvůli stabilitě a opravám chyb
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/cs-CZ/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hlavní změny v této verzi: oprava chyby ohledně šifrovaných zpráv
|
||||
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/de-DE/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/de-DE/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Hauptänderung dieser Version: Beheben eines Problems mit verschlüsselten Nachrichten.
|
||||
Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/en-US/changelogs/40101160.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40101160.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Fix error when sending encrypted message if someone in the room logs out.
|
||||
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.16
|
2
fastlane/metadata/android/et/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: üldiste vigade parandus.
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/et/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Põhilised muutused selles versioonis: krüptitud sõnumitega seotud vigade parandus
|
||||
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/hu-HU/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: leginkább hibajavító és stabilitást növelő frissítés
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/hu-HU/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fő változás ebben a verzióban: titkosított üzenetekkel kapcsolatos hibajavítás
|
||||
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/id/changelogs/40100100.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Versi baru ini terutama berisi perbaikan bug dan peningkatan. Mengirim pesan sekarang jauh lebih cepat.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.10
|
2
fastlane/metadata/android/id/changelogs/40100110.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100110.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Versi baru ini terutama berisi antarmuka pengguna dan peningkatan pengalaman pengguna. Sekarang Anda dapat mengundang teman, dan membuat sebuah DM sangat cepat dengan memindai kode QR.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.11
|
2
fastlane/metadata/android/id/changelogs/40100120.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100120.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Pratinjau URL, keyboard Emoji baru, kemampuan pengaturan ruangan baru, dan salju untuk Natal!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.12
|
2
fastlane/metadata/android/id/changelogs/40100130.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Pratinjau URL, keyboard Emoji baru, kemampuan pengaturan ruangan baru, dan salju untuk Natal!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.13
|
2
fastlane/metadata/android/id/changelogs/40100140.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Edit izin ruangan, tema cahaya/gelap otomatis, dan banyak perbaikan bug.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.14
|
2
fastlane/metadata/android/id/changelogs/40100150.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100150.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Dukungan login sosial.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.15
|
2
fastlane/metadata/android/id/changelogs/40100160.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100160.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: Dukungan login sosial.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.15 dan https://github.com/vector-im/element-android/releases/tag/v1.0.16
|
2
fastlane/metadata/android/id/changelogs/40100170.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40100170.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: perbaikan bug!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.0.17
|
2
fastlane/metadata/android/id/changelogs/40101000.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101000.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: perbaikan VoIP (panggilan audio dan video dalam DM) dan perbaikan bug!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.0
|
2
fastlane/metadata/android/id/changelogs/40101010.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101010.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: peningkatan kinerja dan perbaikan bug!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.1
|
2
fastlane/metadata/android/id/changelogs/40101020.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101020.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: peningkatan kinerja dan perbaikan bug!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.2
|
2
fastlane/metadata/android/id/changelogs/40101030.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101030.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: peningkatan kinerja dan perbaikan bug!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.3
|
2
fastlane/metadata/android/id/changelogs/40101040.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101040.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: peningkatan kinerja dan perbaikan bug!
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.4
|
2
fastlane/metadata/android/id/changelogs/40101050.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: perbaikan hot-fix untuk 1.1.4
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.5
|
2
fastlane/metadata/android/id/changelogs/40101060.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: perbaikan hot-fix untuk 1.1.5
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.6
|
2
fastlane/metadata/android/id/changelogs/40101070.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101070.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: dukungan beta untuk Spaces. Kompres video sebelum mengirim.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.7
|
2
fastlane/metadata/android/id/changelogs/40101080.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101080.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: perbaikan untuk Spaces.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.8
|
2
fastlane/metadata/android/id/changelogs/40101090.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101090.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: menambahkan dukungan untuk jaringan gitter.im.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.9
|
2
fastlane/metadata/android/id/changelogs/40101100.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: pembaruan tema dan gaya dan fitur-fitur baru untuk Spaces.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.10
|
2
fastlane/metadata/android/id/changelogs/40101110.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101110.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: pembaruan tema dan gaya dan fitur baru untuk spaces (perbaikan bug untuk 1.1.10)
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.11
|
2
fastlane/metadata/android/id/changelogs/40101120.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101120.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: pembaruan tema dan gaya dan perbaiki crash setelah panggilan video
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.12
|
2
fastlane/metadata/android/id/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: terutama pembaruan stabilitas dan perbaikan bug.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/id/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Perubahan utama dalam versi ini: memperbaiki masalah tentang pesan terenkripsi.
|
||||
Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
39
fastlane/metadata/android/id/full_description.txt
Normal file
39
fastlane/metadata/android/id/full_description.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi obrolan ini menggunakan enkripsi ujung-ke-ujung untuk memberikan konferensi video, berbagi file, dan panggilan suara.
|
||||
|
||||
<b>Fitur Element termasuk:</b>
|
||||
- Alat komunikasi online yang canggih
|
||||
- Pesan terenkripsi sepenuhnya untuk memungkinkan komunikasi perusahaan yang lebih aman, bahkan untuk pekerja jarak jauh
|
||||
- Obrolan terdesentralisasi berdasarkan framework sumber-terbuka Matrix
|
||||
- Berbagi file dengan aman dengan data terenkripsi saat mengelola proyek
|
||||
- Obrolan video dengan VoIP dan berbagi layar
|
||||
- Integrasi yang mudah dengan alat kolaborasi online favorit Anda, alat manajemen proyek, layanan VoIP dan aplikasi perpesanan tim lainnya
|
||||
|
||||
Element benar-benar berbeda dari aplikasi perpesanan dan kolaborasi lainnya. Ini beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi terdesentralisasi. Ini memungkinkan hosting sendiri untuk memberi pengguna kepemilikan maksimum dan kontrol data dan pesan mereka.
|
||||
|
||||
<b>Pesan privasi dan terenkripsi</b>
|
||||
Element melindungi Anda dari iklan yang tidak diinginkan, data penambangan dan taman berdinding. Ini juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu melalui enkripsi ujung-ke-ujung dan verifikasi perangkat yang di-cross-signed.
|
||||
|
||||
Element memberi Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan aman dengan siapa pun di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan dengan aplikasi seperti Slack.
|
||||
|
||||
<b>Element dapat dihost sendiri</b>
|
||||
Untuk memungkinkan lebih banyak kendali atas data dan percakapan sensitif Anda, Element bisa dihost sendiri atau Anda dapat memilih host berbasis Matrix - standar untuk komunikasi terdesentralisasi sumber-terbuka. Element memberi Anda privasi, kepatuhan keamanan, dan fleksibilitas integrasi.
|
||||
|
||||
<b>Miliki data Anda</b>
|
||||
Anda memutuskan di mana menyimpan data dan pesan Anda. Tanpa risiko penambangan data atau akses dari pihak ketiga.
|
||||
|
||||
Element menempatkan Anda dalam kendali dengan cara yang berbeda:
|
||||
1. Dapatkan akun gratis pada server publik matrix.org yang dihost oleh pengembang Matrix, atau memilih dari ribuan server publik yang dihost oleh sukarelawan
|
||||
2. Host sendiri akun Anda dengan menjalankan server pada infrastruktur IT Anda sendiri
|
||||
3. Daftar untuk akun di server khusus dengan hanya berlangganan platform hosting Element Matrix Services
|
||||
|
||||
<b>Pesan terbuka dan kolaborasi</b>
|
||||
Anda dapat mengobrol dengan siapa saja di jaringan Matrix, apakah mereka menggunakan Element, aplikasi Matrix lain atau bahkan jika mereka menggunakan aplikasi perpesanan yang berbeda.
|
||||
|
||||
<b>Sangat aman</b>
|
||||
Enkripsi ujung-ke-ujung beneran (hanya mereka yang dalam percakapan dapat mendekripsi pesan), dan verifikasi perangkat yang di-cross-signed.
|
||||
|
||||
<b>Komunikasi dan integrasi lengkap</b>
|
||||
Perpesanan, panggilan suara dan video, berbagi file, berbagi layar dan banyak integrasi, bot dan widget. Buat ruangan, komunitas, tetap terhubung dan selesaikan hal-hal.
|
||||
|
||||
<b>Ambil di mana Anda tinggalkan</b>
|
||||
Tetap terhubung di mana pun Anda berada dengan riwayat pesan yang sepenuhnya disinkronkan di semua perangkat Anda dan di web di https://app.element.io
|
1
fastlane/metadata/android/id/short_description.txt
Normal file
1
fastlane/metadata/android/id/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Perpesanan grup - pesan terenkripsi, panggilan grup dan video
|
1
fastlane/metadata/android/id/title.txt
Normal file
1
fastlane/metadata/android/id/title.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Element - Perpesanan Aman
|
2
fastlane/metadata/android/it-IT/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: aggiornamento di stabilità e correzione errori.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/it-IT/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Modifiche principali in questa versione: corretto un problema con i messaggi cifrati.
|
||||
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/pt-BR/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: principalmente atualização de estabilidade e consertos de bug.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/pt-BR/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Principais mudanças nesta versão: consertar um problema sobre mensagens encriptadas.
|
||||
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/ru-RU/changelogs/40101040.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101040.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: улучшение и исправления ошибок!
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.4
|
2
fastlane/metadata/android/ru-RU/changelogs/40101050.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101050.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: исправление для 1.1.4
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.5
|
2
fastlane/metadata/android/ru-RU/changelogs/40101060.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101060.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: исправление для 1.1.5
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.6
|
2
fastlane/metadata/android/ru-RU/changelogs/40101070.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101070.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: бета-поддержка Пространств. Сжатие видео перед отправкой.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.7
|
2
fastlane/metadata/android/ru-RU/changelogs/40101080.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101080.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: Усовершенствованы Пространства!
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.8
|
2
fastlane/metadata/android/ru-RU/changelogs/40101090.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101090.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: добавлена поддержка сети gitter.im.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.9
|
2
fastlane/metadata/android/ru-RU/changelogs/40101100.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101100.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения этой версии: обновлен внешний вид и новые возможности для пространств
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.10
|
2
fastlane/metadata/android/ru-RU/changelogs/40101110.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101110.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения этой версии: обновлен внешний вид и новые возможности для пространств (bugfix для 1.1.10)
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.11
|
2
fastlane/metadata/android/ru-RU/changelogs/40101120.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101120.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: обновление темы и стиля и исправления сбоев после видеовызова
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.12
|
2
fastlane/metadata/android/ru-RU/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: улучшение стабильности и исправления ошибок.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/ru-RU/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/ru-RU/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основные изменения в этой версии: исправление проблемы с зашифрованными сообщениями.
|
||||
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
|
@ -1,30 +1,39 @@
|
|||
Element - это новый тип приложения для обмена сообщениями и совместной работы, которое:
|
||||
Element - это одновременно безопасный мессенджер и приложение для совместной работы, которое идеально подходит для групповых чатов при удаленной работе. Это приложение для чатов использует сквозное шифрование для обеспечения мощных видеоконференций, обмена файлами и голосовых звонков.
|
||||
|
||||
1. Позволяет вам контролировать вашу конфиденциальность
|
||||
2. Позволяет общаться с кем угодно в сети Matrix и даже за ее пределами за счет интеграции с такими приложениями, как Slack
|
||||
3. Защищает вас от рекламы, данных и огороженных стеной садов
|
||||
4. Обеспечивает безопасность с помощью сквозного шифрования с перекрестной подписью для проверки других пользователей
|
||||
<b>Особенности Element включают:</b>
|
||||
- Передовые средства онлайн-общения
|
||||
- Полностью зашифрованные сообщения, обеспечивающие безопасное корпоративное общение даже для удаленных работников
|
||||
- Децентрализованный чат на базе платформы Matrix с открытым исходным кодом
|
||||
- Безопасный обмен файлами с зашифрованными данными при управлении проектами
|
||||
- Видеочаты с VoIP и совместным использованием экрана
|
||||
- Простая интеграция с вашими любимыми инструментами для совместной работы в Интернете, средствами управления проектами, VoIP-сервисами и другими приложениями для обмена сообщениями в команде.
|
||||
|
||||
Element полностью отличается от других приложений для обмена сообщениями и совместной работы, потому что он децентрализован и имеет открытый исходный код.
|
||||
Element полностью отличается от других приложений для обмена сообщениями и совместной работы. Он работает на базе Matrix, открытой сети для безопасного обмена сообщениями и децентрализованного общения. Он позволяет самостоятельно размещать свои данные и сообщения, предоставляя пользователям максимальный контроль над ними.
|
||||
|
||||
Element позволяет вам самостоятельно размещать или выбирать хост-узел, чтобы у вас была конфиденциальность, право собственности и контроль над своими данными и разговорами. Он предоставляет вам доступ к открытой сети, поэтому вы не ограничены общением исключительно с пользователями Element. И он очень надежен и безопаснен.
|
||||
<b>Приватность и зашифрованный обмен сообщениями</b>.
|
||||
Element защищает вас от нежелательной рекламы, сбора данных и "садов". Он также защищает все ваши данные, видео- и голосовую связь один на один благодаря сквозному шифрованию и перекрестной проверке устройств.
|
||||
|
||||
Element может делать все это, потому что он работает на Matrix - стандарте открытого, децентрализованного общения.
|
||||
Element дает вам контроль над вашей конфиденциальностью, позволяя безопасно общаться с любым человеком в сети Matrix или с другими инструментами совместной работы благодаря интеграции с такими приложениями, как Slack.
|
||||
|
||||
Element предоставляет вам полный контроль, позволяя выбрать поставщиков услуг, обслуживающих серверы с вашими беседами. Вы свободны выбрать любой способ размещения прямо из приложения Element:
|
||||
<b>Element может быть размещен самостоятельно</b>.
|
||||
Чтобы обеспечить больший контроль над конфиденциальными данными и разговорами, Element может быть размещен самостоятельно или вы можете выбрать любой хост на базе Matrix - стандарт децентрализованного общения с открытым исходным кодом. Element обеспечивает конфиденциальность, соответствие требованиям безопасности и гибкость интеграции.
|
||||
|
||||
1. Получить бесплатную учетную запись на общедоступном сервере matrix.org, размещенном разработчиками Matrix, или выберите один из тысяч общедоступных серверов, размещенных волонтерами.
|
||||
2. Разместить свою учетную запись на собственном сервере
|
||||
3. Зарегистрироваться на индивидуальном сервере, просто подписавшись на услуги платформы Element Matrix Services
|
||||
<b>Владение своими данными</b>.
|
||||
Вы сами решаете, где хранить свои данные и сообщения. Без риска добычи данных или доступа третьих лиц.
|
||||
|
||||
<b>Почему выбирают Element?</b>
|
||||
Element дает вам возможность контролировать ситуацию различными способами:
|
||||
1. Получить бесплатный аккаунт на публичном сервере matrix.org, размещенном разработчиками Matrix, или выбрать один из тысяч публичных серверов, размещенных добровольцами.
|
||||
2. Самостоятельно разместить свою учетную запись, запустив сервер на собственной IT-инфраструктуре.
|
||||
3. Зарегистрировать учетную запись на пользовательском сервере, просто подписавшись на хостинг-платформу Element Matrix Services.
|
||||
|
||||
<b>СОБСТВЕННЫЕ ДАННЫЕ</b>: Вы решаете, где хранить свои данные и сообщения. Вы владеете ими и контролируете их, а не какая-то мегакорпорация, что собирает ваши данные и предоставляет сторонним лицам доступ к ним.
|
||||
<b>Открытый обмен сообщениями и сотрудничество</b>.
|
||||
Вы можете общаться с любым человеком в сети Matrix, независимо от того, использует ли он Element, другое приложение Matrix или даже если он использует другое приложение для обмена сообщениями.
|
||||
|
||||
<b>ОТКРЫТОЕ ОБЩЕНИЕ И СОТРУДНИЧЕСТВО</b>: Вы можете общаться с кем угодно в сети Matrix, независимо от того, используют ли они приложение Element или другое приложение Matrix, и даже если они используют другую систему обмена сообщениями, такую как Slack, IRC или XMPP.
|
||||
<b>Супербезопасно</b>
|
||||
Настоящее сквозное шифрование (только участники разговора могут расшифровывать сообщения) и проверка устройств с перекрестной подписью.
|
||||
|
||||
<b>СУПЕР-БЕЗОПАСНОСТЬ</b>: Настоящее сквозное шифрование (только участники разговора могут расшифровывать сообщения) и перекрестная подпись для проверки устройств участников разговора.
|
||||
<b>Полная коммуникация и интеграция</b>.
|
||||
Обмен сообщениями, голосовые и видеозвонки, совместное использование файлов, совместное использование экрана и целый ряд интеграций, ботов и виджетов. Создавайте комнаты, сообщества, оставайтесь на связи и выполняйте задачи.
|
||||
|
||||
<b>ПОЛНАЯ КОММУНИКАЦИЯ</b>: Обмен сообщениями, голосовые и видеозвонки, совместное использование файлов, совместное использование экрана и целый ряд интеграций, ботов и виджетов. Создавайте комнаты, сообщества, оставайтесь на связи и добивайтесь результатов.
|
||||
|
||||
<b>ВЫ ВЕЗДЕ</b>: Оставайтесь на связи, где бы вы ни находились, благодаря полностью синхронизированной истории сообщений на всех ваших устройствах и в Интернете по адресу https://app.element.io.
|
||||
<b>Восстанавливайте связь с того места, где остановились</b>.
|
||||
Оставайтесь на связи, где бы вы ни находились, с полностью синхронизированной историей сообщений на всех ваших устройствах и в Интернете по адресу https://app.element.io
|
||||
|
|
|
@ -1 +1 @@
|
|||
Защищённый децентрализованный чат и звонки. Держите ваши данные в безопасности.
|
||||
Групповой мессенджер - зашифрованные сообщения, групповые беседы и видеовызовы
|
||||
|
|
|
@ -1 +1 @@
|
|||
Element (ранее Riot.im)
|
||||
Element - Безопасный мессенджер
|
||||
|
|
2
fastlane/metadata/android/uk/changelogs/40101120.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40101120.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни в цій версії: оновлення теми та стилю та виправлення збоїв після відеовиклику
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.12
|
2
fastlane/metadata/android/uk/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни в цій версії: в поліпшення стабільності та виправлення помилок.
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/uk/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Основні зміни у цій версії: виправлення проблеми із зашифрованими повідомленнями.
|
||||
Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
2
fastlane/metadata/android/zh-TW/changelogs/40101130.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40101130.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:主要是穩定性與臭蟲修復更新。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.1.13
|
2
fastlane/metadata/android/zh-TW/changelogs/40101140.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40101140.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
此版本中的主要變動:修復關於加密訊息的問題。
|
||||
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.1.14
|
|
@ -40,7 +40,63 @@ data class HomeServerCapabilities(
|
|||
*/
|
||||
val roomVersions: RoomVersionCapabilities? = null
|
||||
) {
|
||||
|
||||
enum class RoomCapabilitySupport {
|
||||
SUPPORTED,
|
||||
SUPPORTED_UNSTABLE,
|
||||
UNSUPPORTED,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a feature is supported by the homeserver.
|
||||
* @return
|
||||
* UNKNOWN if the server does not implement room caps
|
||||
* UNSUPPORTED if this feature is not supported
|
||||
* SUPPORTED if this feature is supported by a stable version
|
||||
* SUPPORTED_UNSTABLE if this feature is supported by an unstable version
|
||||
* (unstable version should only be used for dev/experimental purpose)
|
||||
*/
|
||||
fun isFeatureSupported(feature: String): RoomCapabilitySupport {
|
||||
if (roomVersions?.capabilities == null) return RoomCapabilitySupport.UNKNOWN
|
||||
val info = roomVersions.capabilities[feature] ?: return RoomCapabilitySupport.UNSUPPORTED
|
||||
|
||||
val preferred = info.preferred ?: info.support.lastOrNull()
|
||||
val versionCap = roomVersions.supportedVersion.firstOrNull { it.version == preferred }
|
||||
|
||||
return when {
|
||||
versionCap == null -> {
|
||||
RoomCapabilitySupport.UNKNOWN
|
||||
}
|
||||
versionCap.status == RoomVersionStatus.STABLE -> {
|
||||
RoomCapabilitySupport.SUPPORTED
|
||||
}
|
||||
else -> {
|
||||
RoomCapabilitySupport.SUPPORTED_UNSTABLE
|
||||
}
|
||||
}
|
||||
}
|
||||
fun isFeatureSupported(feature: String, byRoomVersion: String): Boolean {
|
||||
if (roomVersions?.capabilities == null) return false
|
||||
val info = roomVersions.capabilities[feature] ?: return false
|
||||
|
||||
return info.preferred == byRoomVersion || info.support.contains(byRoomVersion)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to know if you should force a version when creating
|
||||
* a room that requires this feature.
|
||||
* You can also use #isFeatureSupported prior to this call to check if the
|
||||
* feature is supported and report some feedback to user.
|
||||
*/
|
||||
fun versionOverrideForFeature(feature: String) : String? {
|
||||
val cap = roomVersions?.capabilities?.get(feature)
|
||||
return cap?.preferred ?: cap?.support?.lastOrNull()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
|
||||
const val ROOM_CAP_KNOCK = "knock"
|
||||
const val ROOM_CAP_RESTRICTED = "restricted"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ package org.matrix.android.sdk.api.session.homeserver
|
|||
|
||||
data class RoomVersionCapabilities(
|
||||
val defaultRoomVersion: String,
|
||||
val supportedVersion: List<RoomVersionInfo>
|
||||
val supportedVersion: List<RoomVersionInfo>,
|
||||
// Keys are capabilities defined per spec, as for now knock or restricted
|
||||
val capabilities: Map<String, RoomCapabilitySupport>?
|
||||
)
|
||||
|
||||
data class RoomVersionInfo(
|
||||
|
@ -26,6 +28,11 @@ data class RoomVersionInfo(
|
|||
val status: RoomVersionStatus
|
||||
)
|
||||
|
||||
data class RoomCapabilitySupport(
|
||||
val preferred: String?,
|
||||
val support: List<String>
|
||||
)
|
||||
|
||||
enum class RoomVersionStatus {
|
||||
STABLE,
|
||||
UNSTABLE
|
||||
|
|
|
@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
|||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
|
||||
open class CreateRoomParams {
|
||||
|
@ -162,7 +161,7 @@ open class CreateRoomParams {
|
|||
|
||||
var roomVersion: String? = null
|
||||
|
||||
var joinRuleRestricted: List<RoomJoinRulesAllowEntry>? = null
|
||||
var featurePreset: RoomFeaturePreset? = null
|
||||
|
||||
companion object {
|
||||
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2020 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.create
|
||||
|
||||
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.toContent
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
|
||||
interface RoomFeaturePreset {
|
||||
|
||||
fun updateRoomParams(params: CreateRoomParams)
|
||||
|
||||
fun setupInitialStates(): List<Event>?
|
||||
}
|
||||
|
||||
class RestrictedRoomPreset(val homeServerCapabilities: HomeServerCapabilities, val restrictedList: List<RoomJoinRulesAllowEntry>) : RoomFeaturePreset {
|
||||
|
||||
override fun updateRoomParams(params: CreateRoomParams) {
|
||||
params.historyVisibility = params.historyVisibility ?: RoomHistoryVisibility.SHARED
|
||||
params.guestAccess = params.guestAccess ?: GuestAccess.Forbidden
|
||||
params.roomVersion = homeServerCapabilities.versionOverrideForFeature(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
|
||||
}
|
||||
|
||||
override fun setupInitialStates(): List<Event>? {
|
||||
return listOf(
|
||||
Event(
|
||||
type = EventType.STATE_ROOM_JOIN_RULES,
|
||||
stateKey = "",
|
||||
content = RoomJoinRulesContent(
|
||||
_joinRules = RoomJoinRules.RESTRICTED.value,
|
||||
allowList = restrictedList
|
||||
).toContent()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
|
|||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
|
||||
|
@ -53,7 +54,7 @@ interface StateService {
|
|||
/**
|
||||
* Update the join rule and/or the guest access
|
||||
*/
|
||||
suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?)
|
||||
suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, allowList: List<RoomJoinRulesAllowEntry>? = null)
|
||||
|
||||
/**
|
||||
* Update the avatar of the room
|
||||
|
@ -91,4 +92,8 @@ interface StateService {
|
|||
* @param eventTypes Set of eventType to observe. If empty, all state events will be observed
|
||||
*/
|
||||
fun getStateEventsLive(eventTypes: Set<String>, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData<List<Event>>
|
||||
|
||||
suspend fun setJoinRulePublic()
|
||||
suspend fun setJoinRuleInviteOnly()
|
||||
suspend fun setJoinRuleRestricted(allowList: List<String>)
|
||||
}
|
||||
|
|
|
@ -289,12 +289,12 @@ internal class RealmCryptoStore @Inject constructor(
|
|||
val devicesToDelete = ArrayList<DeviceInfoEntity>()
|
||||
userEntity.devices.iterator().forEach { deviceInfoEntity ->
|
||||
if (deviceInfoEntity.deviceId !in deviceIds) {
|
||||
Timber.d("Remove device ${deviceInfoEntity.deviceId} of user $userId")
|
||||
devicesToDelete.add(deviceInfoEntity)
|
||||
}
|
||||
}
|
||||
while (devicesToDelete.isNotEmpty()) {
|
||||
val device = devicesToDelete.removeAt(0)
|
||||
Timber.d("Remove device ${device.deviceId} of user $userId")
|
||||
device.deleteOnCascade()
|
||||
}
|
||||
// Then update existing devices or add new one
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.mapper
|
|||
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomCapabilitySupport
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionCapabilities
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionInfo
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus
|
||||
|
@ -45,19 +46,28 @@ internal object HomeServerCapabilitiesMapper {
|
|||
roomVersionsJson ?: return null
|
||||
|
||||
return tryOrNull {
|
||||
MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).fromJson(roomVersionsJson)?.let {
|
||||
MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).fromJson(roomVersionsJson)?.let { roomVersions ->
|
||||
RoomVersionCapabilities(
|
||||
defaultRoomVersion = it.default ?: DefaultRoomVersionService.DEFAULT_ROOM_VERSION,
|
||||
supportedVersion = it.available.entries.map { entry ->
|
||||
RoomVersionInfo(
|
||||
version = entry.key,
|
||||
status = if (entry.value == "stable") {
|
||||
RoomVersionStatus.STABLE
|
||||
} else {
|
||||
RoomVersionStatus.UNSTABLE
|
||||
}
|
||||
)
|
||||
}
|
||||
defaultRoomVersion = roomVersions.default ?: DefaultRoomVersionService.DEFAULT_ROOM_VERSION,
|
||||
supportedVersion = roomVersions.available?.entries?.map { entry ->
|
||||
RoomVersionInfo(entry.key, RoomVersionStatus.STABLE
|
||||
.takeIf { entry.value == "stable" }
|
||||
?: RoomVersionStatus.UNSTABLE)
|
||||
}.orEmpty(),
|
||||
capabilities = roomVersions.roomCapabilities?.entries?.mapNotNull { entry ->
|
||||
(entry.value as? Map<*, *>)?.let {
|
||||
val preferred = it["preferred"] as? String ?: return@mapNotNull null
|
||||
val support = (it["support"] as? List<*>)?.filterIsInstance<String>()
|
||||
entry.key to RoomCapabilitySupport(preferred, support.orEmpty())
|
||||
}
|
||||
}?.toMap()
|
||||
// Just for debug purpose
|
||||
// ?: mapOf(
|
||||
// HomeServerCapabilities.ROOM_CAP_RESTRICTED to RoomCapabilitySupport(
|
||||
// preferred = null,
|
||||
// support = listOf("org.matrix.msc3083")
|
||||
// )
|
||||
// )
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,22 @@ internal data class RoomVersions(
|
|||
* Required. A detailed description of the room versions the server supports.
|
||||
*/
|
||||
@Json(name = "available")
|
||||
val available: JsonDict
|
||||
val available: JsonDict? = null,
|
||||
|
||||
/**
|
||||
* "room_capabilities": {
|
||||
* "knock" : {
|
||||
* "preferred": "7",
|
||||
* "support" : ["7"]
|
||||
* },
|
||||
* "restricted" : {
|
||||
* "preferred": "9",
|
||||
* "support" : ["8", "9"]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@Json(name = "room_capabilities")
|
||||
val roomCapabilities: JsonDict? = null
|
||||
)
|
||||
|
||||
// The spec says: If not present, the client should assume that password changes are possible via the API
|
||||
|
|
|
@ -17,17 +17,24 @@
|
|||
package org.matrix.android.sdk.internal.session.permalinks
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixPatterns.getDomain
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||
import java.net.URLEncoder
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
internal class ViaParameterFinder @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val roomGetterProvider: Provider<RoomGetter>
|
||||
private val roomGetterProvider: Provider<RoomGetter>,
|
||||
private val stateEventDataSource: StateEventDataSource
|
||||
) {
|
||||
|
||||
fun computeViaParams(roomId: String, max: Int): List<String> {
|
||||
|
@ -70,4 +77,28 @@ internal class ViaParameterFinder @Inject constructor(
|
|||
.orEmpty()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
fun computeViaParamsForRestricted(roomId: String, max: Int): List<String> {
|
||||
val userThatCanInvite = roomGetterProvider.get().getRoom(roomId)
|
||||
?.getRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.JOIN) })
|
||||
?.map { it.userId }
|
||||
?.filter { userCanInvite(userId, roomId) }
|
||||
.orEmpty()
|
||||
.toSet()
|
||||
|
||||
return userThatCanInvite.map { it.getDomain() }
|
||||
.groupBy { it }
|
||||
.mapValues { it.value.size }
|
||||
.toMutableMap()
|
||||
.let { map -> map.keys.sortedByDescending { map[it] } }
|
||||
.take(max)
|
||||
}
|
||||
|
||||
fun userCanInvite(userId: String, roomId: String): Boolean {
|
||||
val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
|
||||
?.content?.toModel<PowerLevelsContent>()
|
||||
?.let { PowerLevelsHelper(it) }
|
||||
|
||||
return powerLevelsHelper?.isUserAbleToInvite(userId) ?: false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,16 +17,10 @@
|
|||
package org.matrix.android.sdk.internal.session.room.create
|
||||
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
||||
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.toContent
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
||||
import org.matrix.android.sdk.api.session.identity.toMedium
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.util.MimeTypes
|
||||
import org.matrix.android.sdk.internal.crypto.DeviceListManager
|
||||
|
@ -45,7 +39,6 @@ import javax.inject.Inject
|
|||
|
||||
internal class CreateRoomBodyBuilder @Inject constructor(
|
||||
private val ensureIdentityTokenTask: EnsureIdentityTokenTask,
|
||||
private val crossSigningService: CrossSigningService,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val identityStore: IdentityStore,
|
||||
private val fileUploader: FileUploader,
|
||||
|
@ -76,19 +69,18 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (params.joinRuleRestricted != null) {
|
||||
params.roomVersion = "org.matrix.msc3083"
|
||||
params.historyVisibility = params.historyVisibility ?: RoomHistoryVisibility.SHARED
|
||||
params.guestAccess = params.guestAccess ?: GuestAccess.Forbidden
|
||||
}
|
||||
val initialStates = (listOfNotNull(
|
||||
buildEncryptionWithAlgorithmEvent(params),
|
||||
buildHistoryVisibilityEvent(params),
|
||||
buildAvatarEvent(params),
|
||||
buildGuestAccess(params),
|
||||
buildJoinRulesRestricted(params)
|
||||
)
|
||||
+ buildCustomInitialStates(params))
|
||||
params.featurePreset?.updateRoomParams(params)
|
||||
|
||||
val initialStates = (
|
||||
listOfNotNull(
|
||||
buildEncryptionWithAlgorithmEvent(params),
|
||||
buildHistoryVisibilityEvent(params),
|
||||
buildAvatarEvent(params),
|
||||
buildGuestAccess(params)
|
||||
)
|
||||
+ params.featurePreset?.setupInitialStates().orEmpty()
|
||||
+ buildCustomInitialStates(params)
|
||||
)
|
||||
.takeIf { it.isNotEmpty() }
|
||||
|
||||
return CreateRoomBody(
|
||||
|
@ -158,20 +150,6 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun buildJoinRulesRestricted(params: CreateRoomParams): Event? {
|
||||
return params.joinRuleRestricted
|
||||
?.let { allowList ->
|
||||
Event(
|
||||
type = EventType.STATE_ROOM_JOIN_RULES,
|
||||
stateKey = "",
|
||||
content = RoomJoinRulesContent(
|
||||
_joinRules = RoomJoinRules.RESTRICTED.value,
|
||||
allowList = allowList
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the crypto algorithm to the room creation parameters.
|
||||
*/
|
||||
|
|
|
@ -19,8 +19,8 @@ package org.matrix.android.sdk.internal.session.room.state
|
|||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
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
|
||||
|
@ -29,17 +29,20 @@ import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.api.util.MimeTypes
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.session.content.FileUploader
|
||||
import java.lang.UnsupportedOperationException
|
||||
import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder
|
||||
|
||||
internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||
private val stateEventDataSource: StateEventDataSource,
|
||||
private val sendStateTask: SendStateTask,
|
||||
private val fileUploader: FileUploader
|
||||
private val fileUploader: FileUploader,
|
||||
private val viaParameterFinder: ViaParameterFinder
|
||||
) : StateService {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -126,12 +129,19 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?) {
|
||||
override suspend fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, allowList: List<RoomJoinRulesAllowEntry>?) {
|
||||
if (joinRules != null) {
|
||||
if (joinRules == RoomJoinRules.RESTRICTED) throw UnsupportedOperationException("No yet supported")
|
||||
val body = if (joinRules == RoomJoinRules.RESTRICTED) {
|
||||
RoomJoinRulesContent(
|
||||
_joinRules = RoomJoinRules.RESTRICTED.value,
|
||||
allowList = allowList
|
||||
).toContent()
|
||||
} else {
|
||||
mapOf("join_rule" to joinRules)
|
||||
}
|
||||
sendStateEvent(
|
||||
eventType = EventType.STATE_ROOM_JOIN_RULES,
|
||||
body = mapOf("join_rule" to joinRules),
|
||||
body = body,
|
||||
stateKey = null
|
||||
)
|
||||
}
|
||||
|
@ -160,4 +170,20 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
stateKey = null
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setJoinRulePublic() {
|
||||
updateJoinRule(RoomJoinRules.PUBLIC, null)
|
||||
}
|
||||
|
||||
override suspend fun setJoinRuleInviteOnly() {
|
||||
updateJoinRule(RoomJoinRules.INVITE, null)
|
||||
}
|
||||
|
||||
override suspend fun setJoinRuleRestricted(allowList: List<String>) {
|
||||
// we need to compute correct via parameters and check if PL are correct
|
||||
val allowEntries = allowList.map { spaceId ->
|
||||
RoomJoinRulesAllowEntry(spaceId, viaParameterFinder.computeViaParamsForRestricted(spaceId, 3))
|
||||
}
|
||||
updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ Formatter\.formatShortFileSize===1
|
|||
# android\.text\.TextUtils
|
||||
|
||||
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
|
||||
enum class===103
|
||||
enum class===105
|
||||
|
||||
### Do not import temporary legacy classes
|
||||
import org.matrix.android.sdk.internal.legacy.riot===3
|
||||
|
|
|
@ -14,7 +14,7 @@ kapt {
|
|||
// Note: 2 digits max for each value
|
||||
ext.versionMajor = 1
|
||||
ext.versionMinor = 1
|
||||
ext.versionPatch = 15
|
||||
ext.versionPatch = 16
|
||||
|
||||
ext.scVersion = 39
|
||||
|
||||
|
|
|
@ -225,6 +225,7 @@
|
|||
</activity>
|
||||
|
||||
<activity android:name=".features.roomprofile.RoomProfileActivity" />
|
||||
<activity android:name=".features.roomprofile.settings.joinrule.RoomJoinRuleActivity" />
|
||||
|
||||
<activity android:name=".features.roomprofile.members.RoomMemberListActivity" />
|
||||
|
||||
|
|
|
@ -108,6 +108,8 @@ import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
|
|||
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
||||
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
|
||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleChooseRestrictedFragment
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleFragment
|
||||
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
|
||||
import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment
|
||||
import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment
|
||||
|
@ -804,4 +806,14 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(SpaceManageRoomsFragment::class)
|
||||
fun bindSpaceManageRoomsFragment(fragment: SpaceManageRoomsFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(RoomJoinRuleFragment::class)
|
||||
fun bindRoomJoinRuleFragment(fragment: RoomJoinRuleFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(RoomJoinRuleChooseRestrictedFragment::class)
|
||||
fun bindRoomJoinRuleChooseRestrictedFragment(fragment: RoomJoinRuleChooseRestrictedFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
|
|||
import im.vector.app.features.roomprofile.RoomProfileActivity
|
||||
import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleActivity
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet
|
||||
|
@ -169,6 +170,7 @@ interface ScreenComponent {
|
|||
fun inject(activity: SpaceCreationActivity)
|
||||
fun inject(activity: SpaceExploreActivity)
|
||||
fun inject(activity: SpaceManageActivity)
|
||||
fun inject(activity: RoomJoinRuleActivity)
|
||||
|
||||
/* ==========================================================================================
|
||||
* BottomSheets
|
||||
|
|
|
@ -328,11 +328,11 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun renderRecordingWaveform(amplitudeList: List<Int>) {
|
||||
views.voicePlaybackWaveform.apply {
|
||||
post {
|
||||
amplitudeList.forEach { amplitude ->
|
||||
update(amplitude)
|
||||
private fun renderRecordingWaveform(amplitudeList: Array<Int>) {
|
||||
post {
|
||||
views.voicePlaybackWaveform.apply {
|
||||
amplitudeList.iterator().forEach {
|
||||
update(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -409,7 +409,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
|
|||
scaleX = 1f
|
||||
scaleY = 1f
|
||||
translationX = 0f
|
||||
translationY = 0f
|
||||
translationY = 0f
|
||||
}
|
||||
isCancelled?.let {
|
||||
callback?.onVoiceRecordingEnded(it)
|
||||
|
@ -514,7 +514,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
|
|||
override fun onUpdate(state: VoiceMessagePlaybackTracker.Listener.State) {
|
||||
when (state) {
|
||||
is VoiceMessagePlaybackTracker.Listener.State.Recording -> {
|
||||
renderRecordingWaveform(state.amplitudeList)
|
||||
renderRecordingWaveform(state.amplitudeList.toTypedArray())
|
||||
}
|
||||
is VoiceMessagePlaybackTracker.Listener.State.Playing -> {
|
||||
views.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause)
|
||||
|
|
|
@ -40,10 +40,17 @@ class MigrateRoomBottomSheet :
|
|||
VectorBaseBottomSheetDialogFragment<BottomSheetRoomUpgradeBinding>(),
|
||||
MigrateRoomViewModel.Factory {
|
||||
|
||||
enum class MigrationReason {
|
||||
MANUAL,
|
||||
FOR_RESTRICTED
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Args(
|
||||
val roomId: String,
|
||||
val newVersion: String
|
||||
val newVersion: String,
|
||||
val reason: MigrationReason = MigrationReason.MANUAL,
|
||||
val customDescription: CharSequence? = null
|
||||
) : Parcelable
|
||||
|
||||
@Inject
|
||||
|
@ -62,11 +69,22 @@ class MigrateRoomBottomSheet :
|
|||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
views.headerText.setText(if (state.isPublic) R.string.upgrade_public_room else R.string.upgrade_private_room)
|
||||
views.upgradeFromTo.text = getString(R.string.upgrade_public_room_from_to, state.currentVersion, state.newVersion)
|
||||
|
||||
views.autoInviteSwitch.isVisible = !state.isPublic && state.otherMemberCount > 0
|
||||
if (state.migrationReason == MigrationReason.MANUAL) {
|
||||
views.descriptionText.text = getString(R.string.upgrade_room_warning)
|
||||
views.upgradeFromTo.text = getString(R.string.upgrade_public_room_from_to, state.currentVersion, state.newVersion)
|
||||
} else if (state.migrationReason == MigrationReason.FOR_RESTRICTED) {
|
||||
views.descriptionText.setTextOrHide(state.customDescription)
|
||||
views.upgradeFromTo.text = getString(R.string.upgrade_room_for_restricted_note)
|
||||
}
|
||||
|
||||
views.autoUpdateParent.isVisible = state.knownParents.isNotEmpty()
|
||||
if (state.autoMigrateMembersAndParents) {
|
||||
views.autoUpdateParent.isVisible = false
|
||||
views.autoInviteSwitch.isVisible = false
|
||||
} else {
|
||||
views.autoInviteSwitch.isVisible = !state.isPublic && state.otherMemberCount > 0
|
||||
views.autoUpdateParent.isVisible = state.knownParents.isNotEmpty()
|
||||
}
|
||||
|
||||
when (state.upgradingStatus) {
|
||||
is Loading -> {
|
||||
|
@ -143,9 +161,12 @@ class MigrateRoomBottomSheet :
|
|||
const val REQUEST_KEY = "MigrateRoomBottomSheetRequest"
|
||||
const val BUNDLE_KEY_REPLACEMENT_ROOM = "BUNDLE_KEY_REPLACEMENT_ROOM"
|
||||
|
||||
fun newInstance(roomId: String, newVersion: String): MigrateRoomBottomSheet {
|
||||
fun newInstance(roomId: String, newVersion: String,
|
||||
reason: MigrationReason = MigrationReason.MANUAL,
|
||||
customDescription: CharSequence? = null
|
||||
): MigrateRoomBottomSheet {
|
||||
return MigrateRoomBottomSheet().apply {
|
||||
setArguments(Args(roomId, newVersion))
|
||||
setArguments(Args(roomId, newVersion, reason, customDescription))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,11 +90,23 @@ class MigrateRoomViewModel @AssistedInject constructor(
|
|||
copy(upgradingStatus = Loading())
|
||||
}
|
||||
session.coroutineScope.launch {
|
||||
val userToInvite = if (state.autoMigrateMembersAndParents) {
|
||||
summary?.otherMemberIds?.takeIf { !state.isPublic }
|
||||
} else {
|
||||
summary?.otherMemberIds?.takeIf { state.shouldIssueInvites }
|
||||
}.orEmpty()
|
||||
|
||||
val parentSpaceToUpdate = if (state.autoMigrateMembersAndParents) {
|
||||
summary?.flattenParentIds
|
||||
} else {
|
||||
summary?.flattenParentIds?.takeIf { state.shouldUpdateKnownParents }
|
||||
}.orEmpty()
|
||||
|
||||
val result = upgradeRoomViewModelTask.execute(UpgradeRoomViewModelTask.Params(
|
||||
roomId = state.roomId,
|
||||
newVersion = state.newVersion,
|
||||
userIdsToAutoInvite = summary?.otherMemberIds?.takeIf { state.shouldIssueInvites } ?: emptyList(),
|
||||
parentSpaceToUpdate = summary?.flattenParentIds?.takeIf { state.shouldUpdateKnownParents } ?: emptyList(),
|
||||
userIdsToAutoInvite = userToInvite,
|
||||
parentSpaceToUpdate = parentSpaceToUpdate,
|
||||
progressReporter = { indeterminate, progress, total ->
|
||||
setState {
|
||||
copy(
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.airbnb.mvrx.Uninitialized
|
|||
data class MigrateRoomViewState(
|
||||
val roomId: String,
|
||||
val newVersion: String,
|
||||
val customDescription: CharSequence? = null,
|
||||
val currentVersion: String? = null,
|
||||
val isPublic: Boolean = false,
|
||||
val shouldIssueInvites: Boolean = false,
|
||||
|
@ -32,10 +33,15 @@ data class MigrateRoomViewState(
|
|||
val upgradingStatus: Async<UpgradeRoomViewModelTask.Result> = Uninitialized,
|
||||
val upgradingProgress: Int = 0,
|
||||
val upgradingProgressTotal: Int = 0,
|
||||
val upgradingProgressIndeterminate: Boolean = true
|
||||
val upgradingProgressIndeterminate: Boolean = true,
|
||||
val migrationReason: MigrateRoomBottomSheet.MigrationReason = MigrateRoomBottomSheet.MigrationReason.MANUAL,
|
||||
val autoMigrateMembersAndParents: Boolean = false
|
||||
) : MvRxState {
|
||||
constructor(args: MigrateRoomBottomSheet.Args) : this(
|
||||
roomId = args.roomId,
|
||||
newVersion = args.newVersion
|
||||
newVersion = args.newVersion,
|
||||
migrationReason = args.reason,
|
||||
autoMigrateMembersAndParents = args.reason == MigrateRoomBottomSheet.MigrationReason.FOR_RESTRICTED,
|
||||
customDescription = args.customDescription
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,12 +18,13 @@ package im.vector.app.features.roomdirectory.createroom
|
|||
|
||||
import android.net.Uri
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
|
||||
sealed class CreateRoomAction : VectorViewModelAction {
|
||||
data class SetAvatar(val imageUri: Uri?) : CreateRoomAction()
|
||||
data class SetName(val name: String) : CreateRoomAction()
|
||||
data class SetTopic(val topic: String) : CreateRoomAction()
|
||||
data class SetIsPublic(val isPublic: Boolean) : CreateRoomAction()
|
||||
data class SetVisibility(val rule: RoomJoinRules) : CreateRoomAction()
|
||||
data class SetRoomAliasLocalPart(val aliasLocalPart: String) : CreateRoomAction()
|
||||
data class SetIsEncrypted(val isEncrypted: Boolean) : CreateRoomAction()
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.airbnb.mvrx.Fail
|
|||
import com.airbnb.mvrx.Loading
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.dividerItem
|
||||
import im.vector.app.core.epoxy.profiles.buildProfileAction
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.discovery.settingsSectionTitleItem
|
||||
import im.vector.app.features.form.formAdvancedToggleItem
|
||||
|
@ -30,6 +31,7 @@ import im.vector.app.features.form.formSubmitButtonItem
|
|||
import im.vector.app.features.form.formSwitchItem
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreateRoomController @Inject constructor(
|
||||
|
@ -92,24 +94,56 @@ class CreateRoomController @Inject constructor(
|
|||
settingsSectionTitleItem {
|
||||
id("settingsSection")
|
||||
titleResId(R.string.create_room_settings_section)
|
||||
id("visibility")
|
||||
titleResId(R.string.room_settings_room_access_title)
|
||||
}
|
||||
formSwitchItem {
|
||||
id("public")
|
||||
enabled(enableFormElement)
|
||||
title(host.stringProvider.getString(R.string.create_room_public_title))
|
||||
summary(host.stringProvider.getString(R.string.create_room_public_description))
|
||||
switchChecked(viewState.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public)
|
||||
|
||||
listener { value ->
|
||||
host.listener?.setIsPublic(value)
|
||||
when (viewState.roomJoinRules) {
|
||||
RoomJoinRules.INVITE -> {
|
||||
buildProfileAction(
|
||||
id = "joinRule",
|
||||
title = stringProvider.getString(R.string.room_settings_room_access_private_title),
|
||||
subtitle = stringProvider.getString(R.string.room_settings_room_access_private_description),
|
||||
divider = false,
|
||||
editable = true,
|
||||
action = { host.listener?.selectVisibility() }
|
||||
)
|
||||
}
|
||||
RoomJoinRules.PUBLIC -> {
|
||||
buildProfileAction(
|
||||
id = "joinRule",
|
||||
title = stringProvider.getString(R.string.room_settings_room_access_public_title),
|
||||
subtitle = stringProvider.getString(R.string.room_settings_room_access_public_description),
|
||||
divider = false,
|
||||
editable = true,
|
||||
action = { host.listener?.selectVisibility() }
|
||||
)
|
||||
}
|
||||
RoomJoinRules.RESTRICTED -> {
|
||||
buildProfileAction(
|
||||
id = "joinRule",
|
||||
title = stringProvider.getString(R.string.room_settings_room_access_restricted_title),
|
||||
subtitle = stringProvider.getString(R.string.room_create_member_of_space_name_can_join, viewState.parentSpaceSummary?.displayName),
|
||||
divider = false,
|
||||
editable = true,
|
||||
action = { host.listener?.selectVisibility() }
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
// not yet supported
|
||||
}
|
||||
}
|
||||
if (viewState.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public) {
|
||||
|
||||
settingsSectionTitleItem {
|
||||
id("settingsSection")
|
||||
titleResId(R.string.create_room_settings_section)
|
||||
}
|
||||
|
||||
if (viewState.roomJoinRules == RoomJoinRules.PUBLIC) {
|
||||
// Room alias for public room
|
||||
formEditTextItem {
|
||||
id("alias")
|
||||
enabled(enableFormElement)
|
||||
value(viewState.roomVisibilityType.aliasLocalPart)
|
||||
value(viewState.aliasLocalPart)
|
||||
suffixText(":" + viewState.homeServerName)
|
||||
prefixText("#")
|
||||
hint(host.stringProvider.getString(R.string.room_alias_address_hint))
|
||||
|
@ -144,9 +178,9 @@ class CreateRoomController @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
dividerItem {
|
||||
id("divider1")
|
||||
}
|
||||
// dividerItem {
|
||||
// id("divider1")
|
||||
// }
|
||||
formAdvancedToggleItem {
|
||||
id("showAdvanced")
|
||||
title(host.stringProvider.getString(if (viewState.showAdvanced) R.string.hide_advanced else R.string.show_advanced))
|
||||
|
@ -177,7 +211,7 @@ class CreateRoomController @Inject constructor(
|
|||
fun onAvatarChange()
|
||||
fun onNameChange(newName: String)
|
||||
fun onTopicChange(newTopic: String)
|
||||
fun setIsPublic(isPublic: Boolean)
|
||||
fun selectVisibility()
|
||||
fun setAliasLocalPart(aliasLocalPart: String)
|
||||
fun setIsEncrypted(isEncrypted: Boolean)
|
||||
fun toggleShowAdvanced()
|
||||
|
|
|
@ -40,9 +40,12 @@ import im.vector.app.core.resources.ColorProvider
|
|||
import im.vector.app.databinding.FragmentCreateRoomBinding
|
||||
import im.vector.app.features.roomdirectory.RoomDirectorySharedAction
|
||||
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.toOption
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
|
@ -64,6 +67,8 @@ class CreateRoomFragment @Inject constructor(
|
|||
private val viewModel: CreateRoomViewModel by fragmentViewModel()
|
||||
private val args: CreateRoomArgs by args()
|
||||
|
||||
private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel
|
||||
|
||||
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCreateRoomBinding {
|
||||
|
@ -74,6 +79,7 @@ class CreateRoomFragment @Inject constructor(
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
vectorBaseActivity.setSupportActionBar(views.createRoomToolbar)
|
||||
sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java)
|
||||
setupRoomJoinRuleSharedActionViewModel()
|
||||
setupWaitingView()
|
||||
setupRecyclerView()
|
||||
views.createRoomClose.debouncedClicks {
|
||||
|
@ -87,6 +93,16 @@ class CreateRoomFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupRoomJoinRuleSharedActionViewModel() {
|
||||
roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java)
|
||||
roomJoinRuleSharedActionViewModel
|
||||
.observe()
|
||||
.subscribe { action ->
|
||||
viewModel.handle(CreateRoomAction.SetVisibility(action.roomJoinRule))
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
}
|
||||
|
||||
override fun showFailure(throwable: Throwable) {
|
||||
// Note: RoomAliasError are displayed directly in the form
|
||||
if (throwable !is CreateRoomFailure.AliasError) {
|
||||
|
@ -130,9 +146,19 @@ class CreateRoomFragment @Inject constructor(
|
|||
viewModel.handle(CreateRoomAction.SetTopic(newTopic))
|
||||
}
|
||||
|
||||
override fun setIsPublic(isPublic: Boolean) {
|
||||
viewModel.handle(CreateRoomAction.SetIsPublic(isPublic))
|
||||
override fun selectVisibility() = withState(viewModel) { state ->
|
||||
|
||||
val allowed = if (state.supportsRestricted) {
|
||||
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC, RoomJoinRules.RESTRICTED)
|
||||
} else {
|
||||
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC)
|
||||
}
|
||||
RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules, allowed.map { it.toOption(false) })
|
||||
.show(childFragmentManager, "RoomJoinRuleBottomSheet")
|
||||
}
|
||||
// override fun setIsPublic(isPublic: Boolean) {
|
||||
// viewModel.handle(CreateRoomAction.SetIsPublic(isPublic))
|
||||
// }
|
||||
|
||||
override fun setAliasLocalPart(aliasLocalPart: String) {
|
||||
viewModel.handle(CreateRoomAction.SetRoomAliasLocalPart(aliasLocalPart))
|
||||
|
|
|
@ -32,22 +32,28 @@ import im.vector.app.core.extensions.exhaustive
|
|||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||
import im.vector.app.features.raw.wellknown.isE2EByDefault
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixPatterns.getDomain
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.raw.RawService
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
|
||||
import timber.log.Timber
|
||||
|
||||
class CreateRoomViewModel @AssistedInject constructor(@Assisted private val initialState: CreateRoomViewState,
|
||||
private val session: Session,
|
||||
private val rawService: RawService
|
||||
private val rawService: RawService,
|
||||
private val vectorPreferences: VectorPreferences
|
||||
) : VectorViewModel<CreateRoomViewState, CreateRoomAction, CreateRoomViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -58,6 +64,27 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
|
|||
init {
|
||||
initHomeServerName()
|
||||
initAdminE2eByDefault()
|
||||
|
||||
val restrictedSupport = session.getHomeServerCapabilities().isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
|
||||
val createRestricted = when (restrictedSupport) {
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED -> true
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED_UNSTABLE -> vectorPreferences.labsUseExperimentalRestricted()
|
||||
else -> false
|
||||
}
|
||||
|
||||
val defaultJoinRules = if (initialState.parentSpaceId != null && createRestricted) {
|
||||
RoomJoinRules.RESTRICTED
|
||||
} else {
|
||||
RoomJoinRules.INVITE
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
supportsRestricted = createRestricted,
|
||||
roomJoinRules = defaultJoinRules,
|
||||
parentSpaceSummary = initialState.parentSpaceId?.let { session.getRoomSummary(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initHomeServerName() {
|
||||
|
@ -80,7 +107,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
|
|||
|
||||
setState {
|
||||
copy(
|
||||
isEncrypted = roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Private && adminE2EByDefault,
|
||||
isEncrypted = RoomJoinRules.INVITE == roomJoinRules && adminE2EByDefault,
|
||||
hsAdminHasDisabledE2E = !adminE2EByDefault
|
||||
)
|
||||
}
|
||||
|
@ -102,7 +129,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
|
|||
is CreateRoomAction.SetAvatar -> setAvatar(action)
|
||||
is CreateRoomAction.SetName -> setName(action)
|
||||
is CreateRoomAction.SetTopic -> setTopic(action)
|
||||
is CreateRoomAction.SetIsPublic -> setIsPublic(action)
|
||||
is CreateRoomAction.SetVisibility -> setVisibility(action)
|
||||
is CreateRoomAction.SetRoomAliasLocalPart -> setRoomAliasLocalPart(action)
|
||||
is CreateRoomAction.SetIsEncrypted -> setIsEncrypted(action)
|
||||
is CreateRoomAction.Create -> doCreateRoom()
|
||||
|
@ -149,35 +176,45 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
|
|||
|
||||
private fun setTopic(action: CreateRoomAction.SetTopic) = setState { copy(roomTopic = action.topic) }
|
||||
|
||||
private fun setIsPublic(action: CreateRoomAction.SetIsPublic) = setState {
|
||||
if (action.isPublic) {
|
||||
copy(
|
||||
roomVisibilityType = CreateRoomViewState.RoomVisibilityType.Public(""),
|
||||
// Reset any error in the form about alias
|
||||
asyncCreateRoomRequest = Uninitialized,
|
||||
isEncrypted = false
|
||||
)
|
||||
} else {
|
||||
copy(
|
||||
roomVisibilityType = CreateRoomViewState.RoomVisibilityType.Private,
|
||||
isEncrypted = adminE2EByDefault
|
||||
)
|
||||
private fun setVisibility(action: CreateRoomAction.SetVisibility) = setState {
|
||||
when (action.rule) {
|
||||
RoomJoinRules.PUBLIC -> {
|
||||
copy(
|
||||
roomJoinRules = RoomJoinRules.PUBLIC,
|
||||
// Reset any error in the form about alias
|
||||
asyncCreateRoomRequest = Uninitialized,
|
||||
isEncrypted = false
|
||||
)
|
||||
}
|
||||
RoomJoinRules.RESTRICTED -> {
|
||||
copy(
|
||||
roomJoinRules = RoomJoinRules.RESTRICTED,
|
||||
// Reset any error in the form about alias
|
||||
asyncCreateRoomRequest = Uninitialized,
|
||||
isEncrypted = adminE2EByDefault
|
||||
)
|
||||
}
|
||||
// RoomJoinRules.INVITE,
|
||||
// RoomJoinRules.KNOCK,
|
||||
// RoomJoinRules.PRIVATE,
|
||||
else -> {
|
||||
// default to invite
|
||||
copy(
|
||||
roomJoinRules = RoomJoinRules.INVITE,
|
||||
isEncrypted = adminE2EByDefault
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setRoomAliasLocalPart(action: CreateRoomAction.SetRoomAliasLocalPart) {
|
||||
withState { state ->
|
||||
if (state.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public) {
|
||||
setState {
|
||||
copy(
|
||||
roomVisibilityType = CreateRoomViewState.RoomVisibilityType.Public(action.aliasLocalPart),
|
||||
// Reset any error in the form about alias
|
||||
asyncCreateRoomRequest = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
aliasLocalPart = action.aliasLocalPart,
|
||||
// Reset any error in the form about alias
|
||||
asyncCreateRoomRequest = Uninitialized
|
||||
)
|
||||
}
|
||||
// Else ignore
|
||||
}
|
||||
|
||||
private fun setIsEncrypted(action: CreateRoomAction.SetIsEncrypted) = setState { copy(isEncrypted = action.isEncrypted) }
|
||||
|
@ -187,8 +224,7 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
|
|||
return@withState
|
||||
}
|
||||
|
||||
if (state.roomVisibilityType is CreateRoomViewState.RoomVisibilityType.Public
|
||||
&& state.roomVisibilityType.aliasLocalPart.isBlank()) {
|
||||
if (state.roomJoinRules == RoomJoinRules.PUBLIC && state.aliasLocalPart.isNullOrBlank()) {
|
||||
// we require an alias for public rooms
|
||||
setState {
|
||||
copy(asyncCreateRoomRequest = Fail(CreateRoomFailure.AliasError(RoomAliasError.AliasIsBlank)))
|
||||
|
@ -205,15 +241,30 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
|
|||
name = state.roomName.takeIf { it.isNotBlank() }
|
||||
topic = state.roomTopic.takeIf { it.isNotBlank() }
|
||||
avatarUri = state.avatarUri
|
||||
when (state.roomVisibilityType) {
|
||||
is CreateRoomViewState.RoomVisibilityType.Public -> {
|
||||
when (state.roomJoinRules) {
|
||||
RoomJoinRules.PUBLIC -> {
|
||||
// Directory visibility
|
||||
visibility = RoomDirectoryVisibility.PUBLIC
|
||||
// Preset
|
||||
preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
roomAliasName = state.roomVisibilityType.aliasLocalPart
|
||||
roomAliasName = state.aliasLocalPart
|
||||
}
|
||||
is CreateRoomViewState.RoomVisibilityType.Private -> {
|
||||
RoomJoinRules.RESTRICTED -> {
|
||||
state.parentSpaceId?.let {
|
||||
featurePreset = RestrictedRoomPreset(
|
||||
session.getHomeServerCapabilities(),
|
||||
listOf(RoomJoinRulesAllowEntry(
|
||||
state.parentSpaceId,
|
||||
listOf(state.homeServerName)
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
// RoomJoinRules.KNOCK ->
|
||||
// RoomJoinRules.PRIVATE ->
|
||||
// RoomJoinRules.INVITE
|
||||
else -> {
|
||||
// by default create invite only
|
||||
// Directory visibility
|
||||
visibility = RoomDirectoryVisibility.PRIVATE
|
||||
// Preset
|
||||
|
|
|
@ -20,20 +20,24 @@ import android.net.Uri
|
|||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import org.matrix.android.sdk.api.extensions.orTrue
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
||||
data class CreateRoomViewState(
|
||||
val avatarUri: Uri? = null,
|
||||
val roomName: String = "",
|
||||
val roomTopic: String = "",
|
||||
val roomVisibilityType: RoomVisibilityType = RoomVisibilityType.Private,
|
||||
val roomJoinRules: RoomJoinRules = RoomJoinRules.INVITE,
|
||||
val isEncrypted: Boolean = false,
|
||||
val showAdvanced: Boolean = false,
|
||||
val disableFederation: Boolean = false,
|
||||
val homeServerName: String = "",
|
||||
val hsAdminHasDisabledE2E: Boolean = false,
|
||||
val asyncCreateRoomRequest: Async<String> = Uninitialized,
|
||||
val parentSpaceId: String?
|
||||
val parentSpaceId: String?,
|
||||
val parentSpaceSummary: RoomSummary? = null,
|
||||
val supportsRestricted: Boolean = false,
|
||||
val aliasLocalPart: String? = null
|
||||
) : MvRxState {
|
||||
|
||||
constructor(args: CreateRoomArgs) : this(
|
||||
|
@ -47,10 +51,5 @@ data class CreateRoomViewState(
|
|||
fun isEmpty() = avatarUri == null
|
||||
&& roomName.isEmpty()
|
||||
&& roomTopic.isEmpty()
|
||||
&& (roomVisibilityType as? RoomVisibilityType.Public)?.aliasLocalPart?.isEmpty().orTrue()
|
||||
|
||||
sealed class RoomVisibilityType {
|
||||
object Private : RoomVisibilityType()
|
||||
data class Public(val aliasLocalPart: String) : RoomVisibilityType()
|
||||
}
|
||||
&& aliasLocalPart.isNullOrEmpty()
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import im.vector.app.features.roomprofile.RoomProfileArgs
|
|||
import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleActivity
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
|
@ -179,10 +179,8 @@ class RoomSettingsFragment @Inject constructor(
|
|||
.show(childFragmentManager, "RoomHistoryVisibilityBottomSheet")
|
||||
}
|
||||
|
||||
override fun onJoinRuleClicked() = withState(viewModel) { state ->
|
||||
val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules
|
||||
RoomJoinRuleBottomSheet.newInstance(currentJoinRule)
|
||||
.show(childFragmentManager, "RoomJoinRuleBottomSheet")
|
||||
override fun onJoinRuleClicked() {
|
||||
startActivity(RoomJoinRuleActivity.newIntent(requireContext(), roomProfileArgs.roomId))
|
||||
}
|
||||
|
||||
override fun onToggleGuestAccess() = withState(viewModel) { state ->
|
||||
|
|
|
@ -27,6 +27,7 @@ import dagger.assisted.AssistedInject
|
|||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
|
@ -34,6 +35,7 @@ import org.matrix.android.sdk.api.query.QueryStringValue
|
|||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
|
||||
|
@ -44,6 +46,7 @@ import org.matrix.android.sdk.rx.rx
|
|||
import org.matrix.android.sdk.rx.unwrap
|
||||
|
||||
class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val session: Session)
|
||||
: VectorViewModel<RoomSettingsViewState, RoomSettingsAction, RoomSettingsViewEvents>(initialState) {
|
||||
|
||||
|
@ -73,6 +76,24 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||
observeGuestAccess()
|
||||
observeRoomAvatar()
|
||||
observeState()
|
||||
|
||||
val homeServerCapabilities = session.getHomeServerCapabilities()
|
||||
val canUseRestricted = homeServerCapabilities
|
||||
.isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED, room.getRoomVersion())
|
||||
|
||||
val restrictedSupport = homeServerCapabilities.isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
|
||||
val couldUpgradeToRestricted = when (restrictedSupport) {
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED -> true
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED_UNSTABLE -> vectorPreferences.labsUseExperimentalRestricted()
|
||||
else -> false
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
supportsRestricted = canUseRestricted,
|
||||
canUpgradeToRestricted = couldUpgradeToRestricted
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeState() {
|
||||
|
@ -247,8 +268,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||
val summary = state.roomSummary.invoke()
|
||||
|
||||
when (val avatarAction = state.avatarAction) {
|
||||
RoomSettingsViewState.AvatarAction.None -> Unit
|
||||
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
||||
RoomSettingsViewState.AvatarAction.None -> Unit
|
||||
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
||||
operationList.add(room.rx().deleteAvatar())
|
||||
}
|
||||
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
|
||||
|
|
|
@ -43,7 +43,9 @@ data class RoomSettingsViewState(
|
|||
val newHistoryVisibility: RoomHistoryVisibility? = null,
|
||||
val newRoomJoinRules: NewJoinRule = NewJoinRule(),
|
||||
val showSaveAction: Boolean = false,
|
||||
val actionPermissions: ActionPermissions = ActionPermissions()
|
||||
val actionPermissions: ActionPermissions = ActionPermissions(),
|
||||
val supportsRestricted: Boolean = false,
|
||||
val canUpgradeToRestricted: Boolean = false
|
||||
) : MvRxState {
|
||||
|
||||
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.viewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.extensions.addFragment
|
||||
import im.vector.app.core.extensions.commitTransaction
|
||||
import im.vector.app.core.extensions.toMvRxBundle
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.utils.toast
|
||||
import im.vector.app.databinding.ActivitySimpleBinding
|
||||
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
|
||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedActions
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedEvents
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomJoinRuleActivity : VectorBaseActivity<ActivitySimpleBinding>(),
|
||||
RoomJoinRuleChooseRestrictedViewModel.Factory {
|
||||
|
||||
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
||||
|
||||
private lateinit var roomProfileArgs: RoomProfileArgs
|
||||
|
||||
@Inject
|
||||
lateinit var allowListViewModelFactory: RoomJoinRuleChooseRestrictedViewModel.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var errorFormatter: ErrorFormatter
|
||||
|
||||
val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel()
|
||||
|
||||
override fun create(initialState: RoomJoinRuleChooseRestrictedState) = allowListViewModelFactory.create(initialState)
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
roomProfileArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return
|
||||
if (isFirstCreation()) {
|
||||
addFragment(
|
||||
R.id.simpleFragmentContainer,
|
||||
RoomJoinRuleFragment::class.java,
|
||||
roomProfileArgs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.selectSubscribe(this, RoomJoinRuleChooseRestrictedState::updatingStatus) {
|
||||
when (it) {
|
||||
Uninitialized -> {
|
||||
// nop
|
||||
}
|
||||
is Loading -> {
|
||||
views.simpleActivityWaitingView.isVisible = true
|
||||
}
|
||||
is Success -> {
|
||||
withState(viewModel) { state ->
|
||||
if (state.didSwitchToReplacementRoom) {
|
||||
// we should navigate to new room
|
||||
navigator.openRoom(this, state.roomId, null, true)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
views.simpleActivityWaitingView.isVisible = false
|
||||
toast(errorFormatter.toHumanReadable(it.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
RoomJoinRuleChooseRestrictedEvents.NavigateToChooseRestricted -> navigateToChooseRestricted()
|
||||
is RoomJoinRuleChooseRestrictedEvents.NavigateToUpgradeRoom -> navigateToUpgradeRoom(it)
|
||||
}
|
||||
}
|
||||
|
||||
supportFragmentManager.setFragmentResultListener(MigrateRoomBottomSheet.REQUEST_KEY, this) { _, bundle ->
|
||||
bundle.getString(MigrateRoomBottomSheet.BUNDLE_KEY_REPLACEMENT_ROOM)?.let { replacementRoomId ->
|
||||
viewModel.handle(RoomJoinRuleChooseRestrictedActions.SwitchToRoomAfterMigration(replacementRoomId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToUpgradeRoom(events: RoomJoinRuleChooseRestrictedEvents.NavigateToUpgradeRoom) {
|
||||
MigrateRoomBottomSheet.newInstance(
|
||||
events.roomId,
|
||||
events.toVersion,
|
||||
MigrateRoomBottomSheet.MigrationReason.FOR_RESTRICTED,
|
||||
events.description
|
||||
).show(supportFragmentManager, "migrate")
|
||||
}
|
||||
|
||||
private fun navigateToChooseRestricted() {
|
||||
supportFragmentManager.commitTransaction {
|
||||
setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out)
|
||||
val tag = RoomJoinRuleChooseRestrictedFragment::class.simpleName
|
||||
replace(R.id.simpleFragmentContainer,
|
||||
RoomJoinRuleChooseRestrictedFragment::class.java,
|
||||
this@RoomJoinRuleActivity.roomProfileArgs.toMvRxBundle(),
|
||||
tag
|
||||
).addToBackStack(tag)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newIntent(context: Context, roomId: String): Intent {
|
||||
val roomProfileArgs = RoomProfileArgs(roomId)
|
||||
return Intent(context, RoomJoinRuleActivity::class.java).apply {
|
||||
putExtra(MvRx.KEY_ARG, roomProfileArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.list.ItemStyle
|
||||
import im.vector.app.core.ui.list.genericButtonItem
|
||||
import im.vector.app.core.ui.list.genericFooterItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomJoinRuleAdvancedController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val avatarRenderer: AvatarRenderer
|
||||
) : TypedEpoxyController<RoomJoinRuleChooseRestrictedState>() {
|
||||
|
||||
interface InteractionListener {
|
||||
fun didSelectRule(rules: RoomJoinRules)
|
||||
}
|
||||
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
override fun buildModels(state: RoomJoinRuleChooseRestrictedState?) {
|
||||
state ?: return
|
||||
val choices = state.choices ?: return
|
||||
|
||||
val host = this
|
||||
|
||||
genericFooterItem {
|
||||
id("header")
|
||||
text(host.stringProvider.getString(R.string.room_settings_room_access_title))
|
||||
centered(false)
|
||||
style(ItemStyle.TITLE)
|
||||
textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
|
||||
}
|
||||
|
||||
genericFooterItem {
|
||||
id("desc")
|
||||
text(host.stringProvider.getString(R.string.decide_who_can_find_and_join))
|
||||
centered(false)
|
||||
}
|
||||
|
||||
// invite only
|
||||
RoomJoinRuleRadioAction(
|
||||
roomJoinRule = RoomJoinRules.INVITE,
|
||||
description = stringProvider.getString(R.string.room_settings_room_access_private_description),
|
||||
title = stringProvider.getString(R.string.room_settings_room_access_private_invite_only_title),
|
||||
isSelected = state.currentRoomJoinRules == RoomJoinRules.INVITE
|
||||
).toRadioBottomSheetItem().let {
|
||||
it.listener {
|
||||
interactionListener?.didSelectRule(RoomJoinRules.INVITE)
|
||||
// listener?.didSelectAction(action)
|
||||
}
|
||||
add(it)
|
||||
}
|
||||
|
||||
if (choices.firstOrNull { it.rule == RoomJoinRules.RESTRICTED } != null) {
|
||||
val restrictedRule = choices.first { it.rule == RoomJoinRules.RESTRICTED }
|
||||
Timber.w("##@@ ${state.updatedAllowList}")
|
||||
spaceJoinRuleItem {
|
||||
id("restricted")
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
needUpgrade(restrictedRule.needUpgrade)
|
||||
selected(state.currentRoomJoinRules == RoomJoinRules.RESTRICTED)
|
||||
restrictedList(state.updatedAllowList)
|
||||
listener { host.interactionListener?.didSelectRule(RoomJoinRules.RESTRICTED) }
|
||||
}
|
||||
}
|
||||
|
||||
// Public
|
||||
RoomJoinRuleRadioAction(
|
||||
roomJoinRule = RoomJoinRules.PUBLIC,
|
||||
description = stringProvider.getString(R.string.room_settings_room_access_public_description),
|
||||
title = stringProvider.getString(R.string.room_settings_room_access_public_title),
|
||||
isSelected = state.currentRoomJoinRules == RoomJoinRules.PUBLIC
|
||||
).toRadioBottomSheetItem().let {
|
||||
it.listener {
|
||||
interactionListener?.didSelectRule(RoomJoinRules.PUBLIC)
|
||||
}
|
||||
add(it)
|
||||
}
|
||||
|
||||
genericButtonItem {
|
||||
id("save")
|
||||
text("")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,9 +28,18 @@ import kotlinx.parcelize.Parcelize
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
data class JoinRulesOptionSupport(
|
||||
val rule: RoomJoinRules,
|
||||
val needUpgrade: Boolean = false
|
||||
) : Parcelable
|
||||
|
||||
fun RoomJoinRules.toOption(needUpgrade: Boolean) = JoinRulesOptionSupport(this, needUpgrade)
|
||||
|
||||
@Parcelize
|
||||
data class RoomJoinRuleBottomSheetArgs(
|
||||
val currentRoomJoinRule: RoomJoinRules
|
||||
val currentRoomJoinRule: RoomJoinRules,
|
||||
val allowedJoinedRules: List<JoinRulesOptionSupport>
|
||||
) : Parcelable
|
||||
|
||||
class RoomJoinRuleBottomSheet : BottomSheetGeneric<RoomJoinRuleState, RoomJoinRuleRadioAction>() {
|
||||
|
@ -61,9 +70,15 @@ class RoomJoinRuleBottomSheet : BottomSheetGeneric<RoomJoinRuleState, RoomJoinRu
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(currentRoomJoinRule: RoomJoinRules): RoomJoinRuleBottomSheet {
|
||||
fun newInstance(currentRoomJoinRule: RoomJoinRules,
|
||||
allowedJoinedRules: List<JoinRulesOptionSupport> = listOf(
|
||||
RoomJoinRules.INVITE, RoomJoinRules.PUBLIC
|
||||
).map { it.toOption(true) }
|
||||
): RoomJoinRuleBottomSheet {
|
||||
return RoomJoinRuleBottomSheet().apply {
|
||||
setArguments(RoomJoinRuleBottomSheetArgs(currentRoomJoinRule))
|
||||
setArguments(
|
||||
RoomJoinRuleBottomSheetArgs(currentRoomJoinRule, allowedJoinedRules)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class RoomJoinRuleController @Inject constructor(
|
|||
description = stringProvider.getString(R.string.room_settings_room_access_restricted_description),
|
||||
title = span {
|
||||
+stringProvider.getString(R.string.room_settings_room_access_restricted_title)
|
||||
+ " "
|
||||
+" "
|
||||
image(
|
||||
drawableProvider.getDrawable(R.drawable.ic_beta_pill)!!,
|
||||
"bottom"
|
||||
|
@ -59,6 +59,6 @@ class RoomJoinRuleController @Inject constructor(
|
|||
},
|
||||
isSelected = state.currentRoomJoinRule == RoomJoinRules.RESTRICTED
|
||||
)
|
||||
)
|
||||
).filter { state.allowedJoinedRules.map { it.rule }.contains(it.roomJoinRule) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.OnBackPressed
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentJoinRulesRecyclerBinding
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedActions
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomJoinRuleFragment @Inject constructor(
|
||||
val controller: RoomJoinRuleAdvancedController,
|
||||
val avatarRenderer: AvatarRenderer
|
||||
) : VectorBaseFragment<FragmentJoinRulesRecyclerBinding>(),
|
||||
OnBackPressed, RoomJoinRuleAdvancedController.InteractionListener {
|
||||
|
||||
private val viewModel: RoomJoinRuleChooseRestrictedViewModel by activityViewModel()
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||
FragmentJoinRulesRecyclerBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||
val hasUnsavedChanges = withState(viewModel) { it.hasUnsavedChanges }
|
||||
val isLoading = withState(viewModel) { it.updatingStatus is Loading }
|
||||
if (!hasUnsavedChanges || isLoading) {
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.dialog_title_warning)
|
||||
.setMessage(R.string.warning_unsaved_change)
|
||||
.setPositiveButton(R.string.warning_unsaved_change_discard) { _, _ ->
|
||||
requireActivity().finish()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
super.invalidate()
|
||||
controller.setData(state)
|
||||
if (state.hasUnsavedChanges) {
|
||||
// show discard and save
|
||||
views.cancelButton.isVisible = true
|
||||
views.positiveButton.text = getString(R.string.warning_unsaved_change_discard)
|
||||
views.positiveButton.isVisible = true
|
||||
views.positiveButton.text = getString(R.string.save)
|
||||
views.positiveButton.debouncedClicks {
|
||||
viewModel.handle(RoomJoinRuleChooseRestrictedActions.DoUpdateJoinRules)
|
||||
}
|
||||
} else {
|
||||
views.cancelButton.isVisible = false
|
||||
views.positiveButton.isVisible = true
|
||||
views.positiveButton.text = getString(R.string.ok)
|
||||
views.positiveButton.debouncedClicks { requireActivity().finish() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
views.genericRecyclerView.configureWith(controller, hasFixedSize = true)
|
||||
controller.interactionListener = this
|
||||
views.cancelButton.debouncedClicks { requireActivity().finish() }
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
views.genericRecyclerView.cleanup()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun didSelectRule(rules: RoomJoinRules) {
|
||||
val isLoading = withState(viewModel) { it.updatingStatus is Loading }
|
||||
if (isLoading) return
|
||||
|
||||
viewModel.handle(RoomJoinRuleChooseRestrictedActions.SelectJoinRules(rules))
|
||||
}
|
||||
}
|
|
@ -22,10 +22,13 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
|||
|
||||
data class RoomJoinRuleState(
|
||||
val currentRoomJoinRule: RoomJoinRules = RoomJoinRules.INVITE,
|
||||
val allowedJoinedRules: List<JoinRulesOptionSupport> =
|
||||
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC).map { it.toOption(true) },
|
||||
val currentGuestAccess: GuestAccess? = null
|
||||
) : BottomSheetGenericState() {
|
||||
|
||||
constructor(args: RoomJoinRuleBottomSheetArgs) : this(
|
||||
currentRoomJoinRule = args.currentRoomJoinRule
|
||||
currentRoomJoinRule = args.currentRoomJoinRule,
|
||||
allowedJoinedRules = args.allowedJoinedRules
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule
|
||||
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.utils.DebouncedClickListener
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_bottom_sheet_joinrule_restricted)
|
||||
abstract class SpaceJoinRuleItem : VectorEpoxyModel<SpaceJoinRuleItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var selected: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var needUpgrade: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
@EpoxyAttribute
|
||||
var restrictedList: List<MatrixItem> = emptyList()
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
lateinit var listener: ClickListener
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
|
||||
holder.view.onClick(listener)
|
||||
holder.upgradeRequiredButton.setOnClickListener(DebouncedClickListener(listener))
|
||||
|
||||
if (selected) {
|
||||
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on))
|
||||
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked)
|
||||
} else {
|
||||
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off))
|
||||
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_unchecked)
|
||||
}
|
||||
|
||||
holder.upgradeRequiredButton.isVisible = needUpgrade
|
||||
holder.helperText.isVisible = selected
|
||||
|
||||
val items = listOf(holder.space1, holder.space2, holder.space3, holder.space4, holder.space5)
|
||||
holder.spaceMore.isVisible = false
|
||||
items.onEach { it.isVisible = false }
|
||||
if (!needUpgrade) {
|
||||
if (restrictedList.isEmpty()) {
|
||||
holder.listTitle.isVisible = false
|
||||
} else {
|
||||
holder.listTitle.isVisible = true
|
||||
restrictedList.forEachIndexed { index, matrixItem ->
|
||||
if (index < items.size) {
|
||||
items[index].isVisible = true
|
||||
avatarRenderer.render(matrixItem, items[index])
|
||||
} else if (index == items.size) {
|
||||
holder.spaceMore.isVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
holder.listTitle.isVisible = false
|
||||
holder.helperText.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val radioImage by bind<ImageView>(R.id.radioIcon)
|
||||
val actionTitle by bind<TextView>(R.id.actionTitle)
|
||||
val actionDescription by bind<TextView>(R.id.actionDescription)
|
||||
val upgradeRequiredButton by bind<Button>(R.id.upgradeRequiredButton)
|
||||
val listTitle by bind<TextView>(R.id.listTitle)
|
||||
val space1 by bind<ImageView>(R.id.rest1)
|
||||
val space2 by bind<ImageView>(R.id.rest2)
|
||||
val space3 by bind<ImageView>(R.id.rest3)
|
||||
val space4 by bind<ImageView>(R.id.rest4)
|
||||
val space5 by bind<ImageView>(R.id.rest5)
|
||||
val spaceMore by bind<ImageView>(R.id.rest6)
|
||||
val helperText by bind<TextView>(R.id.helperText)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule.advanced
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.loadingItem
|
||||
import im.vector.app.core.epoxy.noResultItem
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.list.genericFooterItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.spaces.manage.roomSelectionItem
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChooseRestrictedController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val avatarRenderer: AvatarRenderer
|
||||
) : TypedEpoxyController<RoomJoinRuleChooseRestrictedState>() {
|
||||
|
||||
interface Listener {
|
||||
fun onItemSelected(matrixItem: MatrixItem)
|
||||
}
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
override fun buildModels(data: RoomJoinRuleChooseRestrictedState?) {
|
||||
data ?: return
|
||||
val host = this
|
||||
|
||||
if (data.filter.isNotEmpty()) {
|
||||
when (val results = data.filteredResults) {
|
||||
Uninitialized,
|
||||
is Fail -> return
|
||||
is Loading -> loadingItem { id("filter_load") }
|
||||
is Success -> {
|
||||
if (results.invoke().isEmpty()) {
|
||||
noResultItem {
|
||||
id("empty")
|
||||
text(host.stringProvider.getString(R.string.no_result_placeholder))
|
||||
}
|
||||
} else {
|
||||
results.invoke().forEach { matrixItem ->
|
||||
roomSelectionItem {
|
||||
id(matrixItem.id)
|
||||
matrixItem(matrixItem)
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
selected(data.updatedAllowList.firstOrNull { it.id == matrixItem.id } != null)
|
||||
itemClickListener { host.listener?.onItemSelected(matrixItem) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// when no filters
|
||||
genericFooterItem {
|
||||
id("h1")
|
||||
text(host.stringProvider.getString(R.string.space_you_know_that_contains_this_room))
|
||||
centered(false)
|
||||
}
|
||||
|
||||
data.possibleSpaceCandidate.forEach { matrixItem ->
|
||||
roomSelectionItem {
|
||||
id(matrixItem.id)
|
||||
matrixItem(matrixItem)
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
selected(data.updatedAllowList.firstOrNull { it.id == matrixItem.id } != null)
|
||||
itemClickListener { host.listener?.onItemSelected(matrixItem) }
|
||||
}
|
||||
}
|
||||
|
||||
if (data.unknownRestricted.isNotEmpty()) {
|
||||
genericFooterItem {
|
||||
id("others")
|
||||
text(host.stringProvider.getString(R.string.other_spaces_or_rooms_you_might_not_know))
|
||||
centered(false)
|
||||
}
|
||||
|
||||
data.unknownRestricted.forEach { matrixItem ->
|
||||
roomSelectionItem {
|
||||
id(matrixItem.id)
|
||||
matrixItem(matrixItem)
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
selected(data.updatedAllowList.firstOrNull { it.id == matrixItem.id } != null)
|
||||
itemClickListener { host.listener?.onItemSelected(matrixItem) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule.advanced
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
sealed class RoomJoinRuleChooseRestrictedActions : VectorViewModelAction {
|
||||
data class FilterWith(val filter: String) : RoomJoinRuleChooseRestrictedActions()
|
||||
data class ToggleSelection(val matrixItem: MatrixItem) : RoomJoinRuleChooseRestrictedActions()
|
||||
data class SelectJoinRules(val rules: RoomJoinRules) : RoomJoinRuleChooseRestrictedActions()
|
||||
object DoUpdateJoinRules : RoomJoinRuleChooseRestrictedActions()
|
||||
data class SwitchToRoomAfterMigration(val roomId: String) : RoomJoinRuleChooseRestrictedActions()
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule.advanced
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
sealed class RoomJoinRuleChooseRestrictedEvents : VectorViewEvents {
|
||||
object NavigateToChooseRestricted : RoomJoinRuleChooseRestrictedEvents()
|
||||
data class NavigateToUpgradeRoom(val roomId: String, val toVersion: String, val description: CharSequence) : RoomJoinRuleChooseRestrictedEvents()
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.OnBackPressed
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentSpaceRestrictedSelectBinding
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.ChooseRestrictedController
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedActions
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomJoinRuleChooseRestrictedFragment @Inject constructor(
|
||||
val controller: ChooseRestrictedController,
|
||||
val avatarRenderer: AvatarRenderer
|
||||
) : VectorBaseFragment<FragmentSpaceRestrictedSelectBinding>(),
|
||||
ChooseRestrictedController.Listener,
|
||||
OnBackPressed {
|
||||
|
||||
private val viewModel: RoomJoinRuleChooseRestrictedViewModel by activityViewModel(RoomJoinRuleChooseRestrictedViewModel::class)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||
FragmentSpaceRestrictedSelectBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
controller.listener = this
|
||||
views.recyclerView.configureWith(controller)
|
||||
views.roomsFilter.queryTextChanges()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.subscribeBy {
|
||||
viewModel.handle(RoomJoinRuleChooseRestrictedActions.FilterWith(it.toString()))
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
|
||||
views.okButton.debouncedClicks {
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
controller.listener = null
|
||||
views.recyclerView.cleanup()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
super.invalidate()
|
||||
controller.setData(state)
|
||||
}
|
||||
|
||||
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||
val filter = views.roomsFilter.query
|
||||
if (filter.isEmpty()) {
|
||||
parentFragmentManager.popBackStack()
|
||||
} else {
|
||||
views.roomsFilter.setQuery("", true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemSelected(matrixItem: MatrixItem) {
|
||||
viewModel.handle(RoomJoinRuleChooseRestrictedActions.ToggleSelection(matrixItem))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule.advanced
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.JoinRulesOptionSupport
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
data class RoomJoinRuleChooseRestrictedState(
|
||||
// the currentRoomId
|
||||
val roomId: String,
|
||||
val roomSummary: Async<RoomSummary> = Uninitialized,
|
||||
val initialRoomJoinRules: RoomJoinRules? = null,
|
||||
val currentRoomJoinRules: RoomJoinRules? = null,
|
||||
val updatedAllowList: List<MatrixItem> = emptyList(),
|
||||
val choices: List<JoinRulesOptionSupport>? = null,
|
||||
val initialAllowList: List<RoomJoinRulesAllowEntry> = emptyList(),
|
||||
val possibleSpaceCandidate: List<MatrixItem> = emptyList(),
|
||||
val unknownRestricted: List<MatrixItem> = emptyList(),
|
||||
val filter: String = "",
|
||||
val filteredResults: Async<List<MatrixItem>> = Uninitialized,
|
||||
val hasUnsavedChanges: Boolean = false,
|
||||
val updatingStatus: Async<Unit> = Uninitialized,
|
||||
val upgradeNeededForRestricted: Boolean = false,
|
||||
val restrictedSupportedByThisVersion: Boolean = false,
|
||||
val restrictedVersionNeeded: String? = null,
|
||||
val didSwitchToReplacementRoom: Boolean = false
|
||||
) : MvRxState {
|
||||
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.roomprofile.settings.joinrule.advanced
|
||||
|
||||
import android.graphics.Typeface
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.styleMatchingText
|
||||
import im.vector.app.features.roomprofile.settings.joinrule.toOption
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
|
||||
class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: RoomJoinRuleChooseRestrictedState,
|
||||
private val session: Session,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val stringProvider: StringProvider
|
||||
) : VectorViewModel<RoomJoinRuleChooseRestrictedState, RoomJoinRuleChooseRestrictedActions, RoomJoinRuleChooseRestrictedEvents>(initialState) {
|
||||
|
||||
var room = session.getRoom(initialState.roomId)!!
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
initializeForRoom(initialState.roomId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeForRoom(roomId: String) {
|
||||
room = session.getRoom(roomId)!!
|
||||
session.getRoomSummary(roomId)?.let { roomSummary ->
|
||||
val joinRulesContent = room.getStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition)
|
||||
?.content
|
||||
?.toModel<RoomJoinRulesContent>()
|
||||
val initialAllowList = joinRulesContent?.allowList
|
||||
|
||||
val knownParentSpacesAllowed = mutableListOf<MatrixItem>()
|
||||
val unknownAllowedOrRooms = mutableListOf<MatrixItem>()
|
||||
initialAllowList.orEmpty().forEach { entry ->
|
||||
val summary = session.getRoomSummary(entry.spaceID)
|
||||
if (summary == null // it's not known by me
|
||||
|| summary.roomType != RoomType.SPACE // it's not a space
|
||||
|| !roomSummary.flattenParentIds.contains(summary.roomId) // it's not a parent space
|
||||
) {
|
||||
unknownAllowedOrRooms.add(
|
||||
summary?.toMatrixItem() ?: MatrixItem.RoomItem(entry.spaceID, null, null)
|
||||
)
|
||||
} else {
|
||||
knownParentSpacesAllowed.add(summary.toMatrixItem())
|
||||
}
|
||||
}
|
||||
|
||||
val possibleSpaceCandidate = knownParentSpacesAllowed.toMutableList()
|
||||
roomSummary.flattenParentIds.mapNotNull {
|
||||
session.getRoomSummary(it)?.toMatrixItem()
|
||||
}.forEach {
|
||||
if (!possibleSpaceCandidate.contains(it)) {
|
||||
possibleSpaceCandidate.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
val homeServerCapabilities = session.getHomeServerCapabilities()
|
||||
var safeRule: RoomJoinRules = joinRulesContent?.joinRules ?: RoomJoinRules.INVITE
|
||||
// server is not really checking that, just to be sure let's check
|
||||
val restrictedSupportedByThisVersion = homeServerCapabilities
|
||||
.isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED, room.getRoomVersion())
|
||||
if (safeRule == RoomJoinRules.RESTRICTED
|
||||
&& !restrictedSupportedByThisVersion) {
|
||||
safeRule = RoomJoinRules.INVITE
|
||||
}
|
||||
|
||||
val restrictedSupport = homeServerCapabilities.isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
|
||||
val couldUpgradeToRestricted = when (restrictedSupport) {
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED -> true
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED_UNSTABLE -> vectorPreferences.labsUseExperimentalRestricted()
|
||||
else -> false
|
||||
}
|
||||
|
||||
val choices = if (restrictedSupportedByThisVersion || couldUpgradeToRestricted) {
|
||||
listOf(
|
||||
RoomJoinRules.INVITE.toOption(false),
|
||||
RoomJoinRules.RESTRICTED.toOption(!restrictedSupportedByThisVersion),
|
||||
RoomJoinRules.PUBLIC.toOption(false)
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
RoomJoinRules.INVITE.toOption(false),
|
||||
RoomJoinRules.PUBLIC.toOption(false)
|
||||
)
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
roomSummary = Success(roomSummary),
|
||||
initialRoomJoinRules = safeRule,
|
||||
currentRoomJoinRules = safeRule,
|
||||
choices = choices,
|
||||
initialAllowList = initialAllowList.orEmpty(),
|
||||
updatedAllowList = initialAllowList.orEmpty().map {
|
||||
session.getRoomSummary(it.spaceID)?.toMatrixItem() ?: MatrixItem.RoomItem(it.spaceID, null, null)
|
||||
},
|
||||
possibleSpaceCandidate = possibleSpaceCandidate,
|
||||
unknownRestricted = unknownAllowedOrRooms,
|
||||
restrictedSupportedByThisVersion = restrictedSupportedByThisVersion,
|
||||
upgradeNeededForRestricted = !restrictedSupportedByThisVersion && couldUpgradeToRestricted,
|
||||
restrictedVersionNeeded = homeServerCapabilities.versionOverrideForFeature(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForChanges() = withState { state ->
|
||||
if (state.initialRoomJoinRules != state.currentRoomJoinRules) {
|
||||
setState {
|
||||
copy(hasUnsavedChanges = true)
|
||||
}
|
||||
return@withState
|
||||
}
|
||||
|
||||
if (state.currentRoomJoinRules == RoomJoinRules.RESTRICTED) {
|
||||
val allowDidChange = state.initialAllowList.map { it.spaceID } != state.updatedAllowList.map { it.id }
|
||||
setState {
|
||||
copy(hasUnsavedChanges = allowDidChange)
|
||||
}
|
||||
return@withState
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(hasUnsavedChanges = false)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(initialState: RoomJoinRuleChooseRestrictedState): RoomJoinRuleChooseRestrictedViewModel
|
||||
}
|
||||
|
||||
override fun handle(action: RoomJoinRuleChooseRestrictedActions) {
|
||||
when (action) {
|
||||
is RoomJoinRuleChooseRestrictedActions.FilterWith -> handleFilter(action)
|
||||
is RoomJoinRuleChooseRestrictedActions.ToggleSelection -> handleToggleSelection(action)
|
||||
is RoomJoinRuleChooseRestrictedActions.SelectJoinRules -> handleSelectRule(action)
|
||||
is RoomJoinRuleChooseRestrictedActions.SwitchToRoomAfterMigration -> handleSwitchToRoom(action)
|
||||
RoomJoinRuleChooseRestrictedActions.DoUpdateJoinRules -> handleSubmit()
|
||||
}.exhaustive
|
||||
checkForChanges()
|
||||
}
|
||||
|
||||
fun handleSubmit() = withState { state ->
|
||||
setState { copy(updatingStatus = Loading()) }
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
when (state.currentRoomJoinRules) {
|
||||
RoomJoinRules.PUBLIC -> room.setJoinRulePublic()
|
||||
RoomJoinRules.INVITE -> room.setJoinRuleInviteOnly()
|
||||
RoomJoinRules.RESTRICTED -> room.setJoinRuleRestricted(state.updatedAllowList.map { it.id })
|
||||
RoomJoinRules.KNOCK,
|
||||
RoomJoinRules.PRIVATE,
|
||||
null -> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
setState { copy(updatingStatus = Success(Unit)) }
|
||||
} catch (failure: Throwable) {
|
||||
setState { copy(updatingStatus = Fail(failure)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSelectRule(action: RoomJoinRuleChooseRestrictedActions.SelectJoinRules) = withState { state ->
|
||||
val currentRoomJoinRules = state.currentRoomJoinRules
|
||||
|
||||
val candidate = session.getRoomSummary(state.roomId)
|
||||
?.flattenParentIds
|
||||
?.filter {
|
||||
session.getRoomSummary(it)?.spaceChildren?.firstOrNull { it.childRoomId == state.roomId } != null
|
||||
}?.mapNotNull {
|
||||
session.getRoomSummary(it)?.toMatrixItem()
|
||||
}?.firstOrNull()
|
||||
val description = if (candidate != null) {
|
||||
stringProvider.getString(R.string.upgrade_room_for_restricted, candidate.getBestName()).toSpannable().let {
|
||||
it.styleMatchingText(candidate.getBestName(), Typeface.BOLD)
|
||||
}
|
||||
} else {
|
||||
stringProvider.getString(R.string.upgrade_room_for_restricted_no_param)
|
||||
}
|
||||
|
||||
if (action.rules == RoomJoinRules.RESTRICTED && state.upgradeNeededForRestricted) {
|
||||
// let's show the room upgrade bottom sheet
|
||||
_viewEvents.post(
|
||||
RoomJoinRuleChooseRestrictedEvents.NavigateToUpgradeRoom(
|
||||
state.roomId,
|
||||
state.restrictedVersionNeeded ?: "",
|
||||
description
|
||||
)
|
||||
)
|
||||
return@withState
|
||||
}
|
||||
|
||||
if (action.rules == RoomJoinRules.RESTRICTED && currentRoomJoinRules != RoomJoinRules.RESTRICTED) {
|
||||
// switching to restricted
|
||||
// if allow list is empty, then default to current space parents
|
||||
if (state.updatedAllowList.isEmpty()) {
|
||||
val candidates = session.getRoomSummary(state.roomId)
|
||||
?.flattenParentIds
|
||||
?.filter {
|
||||
session.getRoomSummary(it)?.spaceChildren?.firstOrNull { it.childRoomId == state.roomId } != null
|
||||
}?.mapNotNull {
|
||||
session.getRoomSummary(it)?.toMatrixItem()
|
||||
}.orEmpty()
|
||||
setState {
|
||||
copy(updatedAllowList = candidates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
currentRoomJoinRules = action.rules
|
||||
)
|
||||
}
|
||||
|
||||
if (action.rules == RoomJoinRules.RESTRICTED && currentRoomJoinRules == RoomJoinRules.RESTRICTED) {
|
||||
_viewEvents.post(RoomJoinRuleChooseRestrictedEvents.NavigateToChooseRestricted)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSwitchToRoom(action: RoomJoinRuleChooseRestrictedActions.SwitchToRoomAfterMigration) = withState { state ->
|
||||
viewModelScope.launch {
|
||||
val oldRoomSummary = session.getRoomSummary(state.roomId)
|
||||
val replacementRoomSummary = session.getRoomSummary(action.roomId)
|
||||
setState {
|
||||
copy(
|
||||
roomId = action.roomId,
|
||||
roomSummary = replacementRoomSummary?.let { Success(it) } ?: Uninitialized,
|
||||
didSwitchToReplacementRoom = true
|
||||
)
|
||||
}
|
||||
initializeForRoom(action.roomId)
|
||||
// set as restricted now
|
||||
val candidates = oldRoomSummary
|
||||
?.flattenParentIds
|
||||
?.filter {
|
||||
session.getRoomSummary(it)?.spaceChildren?.firstOrNull { it.childRoomId == state.roomId } != null
|
||||
}?.mapNotNull {
|
||||
session.getRoomSummary(it)?.toMatrixItem()
|
||||
}.orEmpty()
|
||||
setState {
|
||||
copy(
|
||||
currentRoomJoinRules = RoomJoinRules.RESTRICTED,
|
||||
updatedAllowList = candidates
|
||||
)
|
||||
}
|
||||
setState { copy(updatingStatus = Loading()) }
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
room.setJoinRuleRestricted(candidates.map { it.id })
|
||||
setState { copy(updatingStatus = Success(Unit)) }
|
||||
} catch (failure: Throwable) {
|
||||
setState { copy(updatingStatus = Fail(failure)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleToggleSelection(action: RoomJoinRuleChooseRestrictedActions.ToggleSelection) = withState { state ->
|
||||
val selection = state.updatedAllowList.toMutableList()
|
||||
if (selection.indexOfFirst { action.matrixItem.id == it.id } != -1) {
|
||||
selection.removeAll { it.id == action.matrixItem.id }
|
||||
} else {
|
||||
selection.add(action.matrixItem)
|
||||
}
|
||||
val unknownAllowedOrRooms = mutableListOf<MatrixItem>()
|
||||
|
||||
// we would like to keep initial allowed here to show them unchecked
|
||||
// to make it easier for users to spot the changes
|
||||
val union = mutableListOf<MatrixItem>().apply {
|
||||
addAll(
|
||||
state.initialAllowList.map {
|
||||
session.getRoomSummary(it.spaceID)?.toMatrixItem() ?: MatrixItem.RoomItem(it.spaceID, null, null)
|
||||
}
|
||||
)
|
||||
addAll(selection)
|
||||
}.distinctBy { it.id }.sortedBy { it.id }
|
||||
|
||||
union.forEach { entry ->
|
||||
val summary = session.getRoomSummary(entry.id)
|
||||
if (summary == null) {
|
||||
unknownAllowedOrRooms.add(
|
||||
entry
|
||||
)
|
||||
} else if (summary.roomType != RoomType.SPACE) {
|
||||
unknownAllowedOrRooms.add(entry)
|
||||
} else if (!state.roomSummary.invoke()!!.flattenParentIds.contains(entry.id)) {
|
||||
// it's a space but not a direct parent
|
||||
unknownAllowedOrRooms.add(entry)
|
||||
} else {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
updatedAllowList = selection.toList(),
|
||||
unknownRestricted = unknownAllowedOrRooms
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFilter(action: RoomJoinRuleChooseRestrictedActions.FilterWith) = withState { state ->
|
||||
setState {
|
||||
copy(filter = action.filter, filteredResults = Loading())
|
||||
}
|
||||
viewModelScope.launch {
|
||||
if (vectorPreferences.developerMode()) {
|
||||
// in developer mode we let you choose any room or space to restrict to
|
||||
val filteredCandidates = session.getRoomSummaries(
|
||||
roomSummaryQueryParams {
|
||||
excludeType = null
|
||||
displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE)
|
||||
memberships = listOf(Membership.JOIN)
|
||||
}
|
||||
).map { it.toMatrixItem() }
|
||||
setState {
|
||||
copy(
|
||||
filteredResults = Success(filteredCandidates)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// in normal mode you can only restrict to space parents
|
||||
setState {
|
||||
copy(
|
||||
filteredResults = Success(
|
||||
session.getRoomSummary(state.roomId)?.flattenParentIds?.mapNotNull {
|
||||
session.getRoomSummary(it)?.toMatrixItem()
|
||||
}?.filter {
|
||||
it.displayName?.contains(filter, true) == true
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomJoinRuleChooseRestrictedViewModel, RoomJoinRuleChooseRestrictedState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleChooseRestrictedState)
|
||||
: RoomJoinRuleChooseRestrictedViewModel? {
|
||||
val factory = when (viewModelContext) {
|
||||
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
|
||||
is ActivityViewModelContext -> viewModelContext.activity as? Factory
|
||||
}
|
||||
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,11 +24,13 @@ import im.vector.app.features.settings.VectorPreferences
|
|||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.raw.RawService
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -93,13 +95,26 @@ class CreateSpaceViewModelTask @Inject constructor(
|
|||
}
|
||||
)
|
||||
} else {
|
||||
if (vectorPreferences.labsUseExperimentalRestricted()) {
|
||||
val homeServerCapabilities = session
|
||||
.getHomeServerCapabilities()
|
||||
val restrictedSupport = homeServerCapabilities
|
||||
.isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
|
||||
|
||||
val createRestricted = when (restrictedSupport) {
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED -> true
|
||||
HomeServerCapabilities.RoomCapabilitySupport.SUPPORTED_UNSTABLE -> vectorPreferences.labsUseExperimentalRestricted()
|
||||
else -> false
|
||||
}
|
||||
if (createRestricted) {
|
||||
session.createRoom(CreateRoomParams().apply {
|
||||
this.name = roomName
|
||||
this.joinRuleRestricted = listOf(
|
||||
RoomJoinRulesAllowEntry(
|
||||
spaceID = spaceID,
|
||||
via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
|
||||
this.featurePreset = RestrictedRoomPreset(
|
||||
homeServerCapabilities,
|
||||
listOf(
|
||||
RoomJoinRulesAllowEntry(
|
||||
spaceID = spaceID,
|
||||
via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
|
||||
)
|
||||
)
|
||||
)
|
||||
if (e2eByDefault) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue